Reflection.Emit против CodeDOM

Какие плюсы и минусы использования библиотеки Reflection.Emit по сравнению с CodeDOM для динамической генерации кода во время выполнения?

Я пытаюсь создать некоторые (относительно сложные) динамические классы в системе на основе метаданных, доступных во время выполнения в форме XML. Я буду создавать классы, которые расширяют существующие классы в сборке приложения, реализуют дополнительные интерфейсы, добавляют методы и переопределяют виртуальные и абстрактные члены.

Прежде чем углубляться в реализацию, я хочу убедиться, что выбрал соответствующую технику. Была бы полезна любая информация о том, чем отличаются эти разные методы генерации кода. Кроме того, будет полезна любая информация о библиотеках с открытым исходным кодом, которые упрощают или оптимизируют работу с любым API.


person LBushkin    schedule 02.03.2010    source источник
comment
Когда я прочитал это, мне в голову пришло первое, что пришло мне в голову. Стоит ли посмотреть, как они это делают?   -  person quip    schedule 03.03.2010
comment
Я действительно смотрю на это. Они используют Reflection.Emit, но непонятно, почему они выбрали это вместо CodeDOM.   -  person LBushkin    schedule 03.03.2010
comment
Сегодня большое отличие состоит в том, что CSharpCodeProvider API доступны только в .NET Core 3.0 и новее. Они даже не являются частью .NET Standard (в отличие от Reflection.Emit)   -  person nawfal    schedule 13.04.2020


Ответы (3)


Я думаю, что основные моменты CodeDOM и Reflection.Emit следующие:

  • CodeDom генерирует исходный код C # и обычно используется при генерации кода, который должен быть включен как часть решения и скомпилирован в IDE (например, классы LINQ to SQL, WSDL, XSD работают таким образом). В этом сценарии вы также можете использовать частичные классы для настройки сгенерированного кода. Он менее эффективен, потому что он генерирует исходный код C #, а затем запускает компилятор, чтобы проанализировать его (снова!) И скомпилировать его. Вы можете сгенерировать код, используя конструкции относительно высокого уровня (аналогичные выражениям и операторам C #), например циклы.

  • Reflection.Emit генерирует IL, поэтому он напрямую создает сборку, которая также может храниться только в памяти. В результате это намного эффективнее. Вам нужно сгенерировать низкоуровневый IL-код (значения хранятся в стеке; цикл должен быть реализован с использованием переходов), поэтому создание более сложной логики немного затруднительно.

В общем, я думаю, что Reflection.Emit обычно считается предпочтительным способом генерации кода во время выполнения, в то время как CodeDOM предпочтительнее при генерации кода перед компиляцией. В вашем сценарии оба они, вероятно, будут работать нормально (хотя CodeDOM могут потребовать более высоких привилегий, потому что на самом деле ему нужно вызвать компилятор C #, который является частью любой установки .NET).

Другой вариант - использовать Expression класс. В .NET 4.0 он позволяет создавать код, эквивалентный выражениям и операторам C #. Однако он не позволяет создавать классы. Таким образом, вы можете объединить это с Reflection.Emit (для создания классов, которые делегируют реализацию коду, сгенерированному с использованием Expression). Для некоторых сценариев вам также может не потребоваться полная иерархия классов - часто может быть достаточно словаря динамически сгенерированных делегатов, таких как Dictionary<string, Action> (но, конечно, это зависит от вашего конкретного сценария).

person Tomas Petricek    schedule 02.03.2010
comment
Насколько я понимаю, CodeDOM также может генерировать код в памяти? - person Steven Jeuris; 26.09.2011
comment
Не могли бы вы подробнее рассказать, как можно использовать частичные классы с CodeDom? Если мое исследование на что-то указывает, так это на то, что вы вообще не можете использовать partial: это даже не опция в TypeAttributes перечислении. - person Jeroen Vannevel; 07.04.2014
comment
Полный ответ. Спасибо. - person nawfal; 19.08.2015

Код, ориентированный на CodeDom, легче поддерживать, поскольку вы генерируете код C #, а не IL (C # может читать больше людей, чем IL). Более того, если вы ошиблись в коде CodeDom, вы получите ошибку компилятора; если вы генерируете недопустимый IL, вы получаете фатальное исключение или сбой.

Однако, поскольку CodeDom вызывает компилятор csc.exe, подготовка кода к использованию выполняется немного медленнее. С Reflection.Emit вы можете генерировать код прямо в памяти.

CodeDom, вероятно, подходит для большинства вещей; XmlSerializer и конструктор WinForms используют его.

person Tim Robinson    schedule 02.03.2010
comment
Для чего XmlSerializer использует CodeDOM? Извините, я мог найти это в Reflector, но я ленив, так как мое внимание разделено между многими другими проблемами, и, поскольку вы ответили на этот вопрос, я предполагаю, что вы уже потратили время, чтобы вы могли просто ввести ответ, не тратя на него слишком много времени либо. - person Water Cooler v2; 02.12.2013
comment
@ WaterCoolerv2 Если бы я догадался, они создают сборку для чтения / записи целевых типов. Однажды меня привлекли к проекту с чудовищным уровнем доступа к данным, основанным на отражении. Это было очень медленно, поэтому пользователи ненавидели это, но никто не знал, как это исправить. Я сделал это практически мгновенно с помощью комбинации кеширования и Emit. Помимо исключения медленного отражения из картины, вы также можете устранить накладные расходы, сначала проверив типы и выполнив соответствующие коды операций непосредственно в методе, вместо того, чтобы выполнять ветвление или вызывать специализированные делегаты во время процесса сериализации. - person Daniel; 08.01.2018

Вы можете посмотреть _1 _. Однако это только .NET 4.0.

person Vlad    schedule 02.03.2010