Reflection - динамическое создание кода
От: Sorantis Швеция  
Дата: 26.03.07 14:25
Оценка:
Натолкнулся в нете на следующую статейку.
Иногда нам требуется динамически создать некий код. Естественно, что этот код у нас будет храниться в некоторой сборке. При этом сама созданная динамически сборка может существовать только в памяти или же может быть сохраненной на диск в виде файла. Сейчас мы посмотрим, как это можно сделать.
Для начала небольшое замечание по порядку создания соответствующих объектов. Сначала мы должны сгенерировать сборку, затем на основании этой сборки - модуль, потом на основании этого модуля - тип (например, класс), потом на основании этого типа (класса) - его члены (конструкторы, методы и т. п.). И, уже в самом конце, мы создаем непосредственно сгенерированный на предыдущих шагах тип.
Вот пример такого кода:
[c#]
            // Создание имени сборки.
            AssemblyName an = new AssemblyName("MyAssembly");
            an.Version = new Version("1.0.0.0");

            // Создание сборки.            
            AssemblyBuilder ab;
            ab = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Save);
            
            // Создание модуля в сборке.
            ModuleBuilder mb = ab.DefineDynamicModule("MyModule", "My.dll");

            // Создание типа в сборке.
            TypeBuilder tb = mb.DefineType("MyClass", TypeAttributes.Public);

            // Создание конструктора без параметров.
            ConstructorBuilder cb0 = tb.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, null);

            // Добавление кода для конструктора.
            ILGenerator il0 = cb0.GetILGenerator();
            il0.Emit(OpCodes.Ret);

            // Создание конструктора с параметром типа string.
            ConstructorBuilder cb = tb.DefineConstructor(MethodAttributes.Public,
                CallingConventions.Standard, new Type[] { typeof(string)});
            // Добавление кода для конструктора.
            ILGenerator il = cb.GetILGenerator();
            il.EmitWriteLine("Constructor");
            il.Emit(OpCodes.Ret);

            // Непосредственное создание типа.
            tb.CreateType();

            // Сохранение типа в файл.
            ab.Save("qqq.dll");
[/c#]

Приведенный код при запуске создаст на жестком диске файл qqq.dll, в котором будет класс с 2-я конструкторами, причем второй конструктор будет при вызове выводить строчку "Constructor".
Несколько пояснений по коду.
Первое. Очень часто при создании типов и членов этих типов надо указать их атрибуты (модификаторы доступа типа public и т. п.). Это мы делаем через перечисления TypeAttributes и MethodAttributes, которое содержит соотвествующие значения (Public, например). Несколько необходимых значений из этих перечислений можно соединить через побитовое "или".
Второе. Метод Emit класса ILGenerator в качестве параметра принимает перечисление OpCodes, которое фактически содержит инструкции на языке IL - языке, который является аналогом для .NET обычного ассемблера. Это означает, что его инструкции не столь очевидны для реального программирования - именно поэтому в качестве примера таких IL-инструкций и была приведена самая простая из них - а именно выход из функции (OpCodes.Ret).


Кто нибудь применял данную методику? Если да то в каких целях? В каких задачах он используется?
Заранее спасибо
As long as there is life, there is hope
Re: Reflection - динамическое создание кода
От: nikov США http://www.linkedin.com/in/nikov
Дата: 26.03.07 14:29
Оценка: 2 (1)
Здравствуйте, Sorantis, Вы писали:

S>Кто нибудь применял данную методику? Если да то в каких целях? В каких задачах он используется?

S>Заранее спасибо

Конечно, применяли.
Очень полезная вещь.
Ищите в гугле по словам Reflection.Emit.
Достаточно много примеров есть в MSDN и на этом форуме.
Re: Reflection - динамическое создание кода
От: Lloyd Россия  
Дата: 26.03.07 14:31
Оценка: 2 (1)
Здравствуйте, Sorantis, Вы писали:

S>Кто нибудь применял данную методику? Если да то в каких целях? В каких задачах он используется?

S>Заранее спасибо

здесь
Автор(ы): Андрей Мартынов
Дата: 14.03.2003
В различных технологиях программирования используются метаданные разной степени подробности и разной степени универсальности. Но в технологии .Net метаданные играют совершенно особую роль. Это роль "универсального клея", на который возложены функции поставщика информации о типах как во время компиляции программы, так и во время её исполнения.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re: Reflection - динамическое создание кода
От: damat_ae1  
Дата: 26.03.07 15:20
Оценка:
da, primenyali za4astuu eto trebuetsya dlya aspektov. tipa takaya realizaciya.
v osnovnom tebe budet nado generit decorator — realizaciyu interfeisa s indirections ili pronasledovanyi klass — togda vyzovy bazovyh metodov i
povtorenie konstructorov. OpCodes enumeration — o4en neudoben u4ityvaya stekovuu arhitekturu kommand. sdes bolee umestno prototipirovanie kak v
imperativnyh yazykah — [conditional execution] = if([bool expression]) {[execution]} esle {[execution]}. Eto privedet k poyavleniyu paralelnyh
ierarhiy klassov, dubliruus4ih logiku kompilera.
potomu lu4she ispolzovat CODEDOM esli kone4no izvestny kontrakty(WCF).

a es4e takoe — lubaya popytka tak reshat problemu — klinit aspect — resultat oshibki proektirovaniya — command pattern — s nim vse aspekty
prosto dobavlautsya kak template method.
Re: Reflection - динамическое создание кода
От: rameel https://github.com/rsdn/CodeJam
Дата: 26.03.07 15:33
Оценка: +2
Здравствуйте, Sorantis, Вы писали:

Советую взглянуть на EmitHelper из состава BLToolkit (форум)
... << RSDN@Home 1.2.0 alpha rev. 669>>
Динамическое создание кода - зачем?
От: nikov США http://www.linkedin.com/in/nikov
Дата: 27.03.07 07:13
Оценка: 17 (2) +1
#Имя: FAQ.dotnet.reflection.emit
Здравствуйте, Sorantis, Вы писали:

S>Кто нибудь применял данную методику? Если да то в каких целях? В каких задачах он используется?

S>Заранее спасибо

Бывают ситуации, когда в момент написания программы для некоторых типов возможно описать только интерфейсы (или даже обобщенные интерфейсы, используя generics). Поведение, то есть логику методов, возможно будет определить лишь на этапе исполнения, например, на основе информации, считанной из базы данных. В этом случае мы пишем статический высокоуровневый код, оперирующий с объектами через интерфейсы ("честно", без использования reflection). Во время исполнения программы с помощью Reflection.Emit динамически определяются классы, реализующие имеющиеся интерфейсы, создаются их экземпляры (например, с помощью класса System.Activator), которые и передаются этому высокоуровневому коду. Более подробно данная методика описана, например, в этой статье.

Ключевой момент в этой методике: формирование правильного CIL (Common Intermediate Language, или, как его еще называют — IL, MSIL). Как в нем разобраться? Большую помощь может оказать прочтение спецификации: Ecma-335, Partition III. Также полезно взять код на языке высокого уровня, например, C# или Nemerle (превед немерлистам! ), скомпилировать его и изучить CIL-код получившейся сборки с помощью Reflector'а или ildasm. Последний должен ставиться вместе с .NET SDK или Visual Studio .NET, например, на моем компьютере он находится по такому пути:

C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\ildasm.exe


Идея очень проста: у каждого метода существует свой стек вычислений (evaluation stack, не путать со стеком вызовов методов!). Он устроен наподобие стека у советских программируемых калькуляторов: есть инструкции, которые кладут операнды на вершину стека, и есть инструкции, которые снимают один или несколько операндов оттуда, заменяя их результатом вычисления соответствующей операции.

В .NET 2.0 появилась возможность создавать временные динамические методы без создания динамической сборки с помощью класса DynamicMethod. Читайте раздел MSDN, посвященный Reflection.Emit. Успехов!
Re[2]: Reflection - динамическое создание кода
От: Sorantis Швеция  
Дата: 27.03.07 13:27
Оценка:
Здравствуйте, nikov, Вы писали:

N>Здравствуйте, Sorantis, Вы писали:


S>>Кто нибудь применял данную методику? Если да то в каких целях? В каких задачах он используется?

S>>Заранее спасибо

N>Бывают ситуации, когда в момент написания программы для некоторых типов возможно описать только интерфейсы (или даже обобщенные интерфейсы, используя generics). Поведение, то есть логику методов, возможно будет определить лишь на этапе исполнения, например, на основе информации, считанной из базы данных. В этом случае мы пишем статический высокоуровневый код, оперирующий с объектами через интерфейсы ("честно", без использования reflection). Во время исполнения программы с помощью Reflection.Emit динамически определяются классы, реализующие имеющиеся интерфейсы, создаются их экземпляры (например, с помощью класса System.Activator), которые и передаются этому высокоуровневому коду. Более подробно данная методика описана, например, в этой статье.


N>Ключевой момент в этой методике: формирование правильного CIL (Common Intermediate Language, или, как его еще называют — IL, MSIL). Как в нем разобраться? Большую помощь может оказать прочтение спецификации: Ecma-335, Partition III. Также полезно взять код на языке высокого уровня, например, C# или Nemerle (превед немерлистам! ), скомпилировать его и изучить CIL-код получившейся сборки с помощью Reflector'а или ildasm. Последний должен ставиться вместе с .NET SDK или Visual Studio .NET, например, на моем компьютере он находится по такому пути:


N>
N>C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\ildasm.exe
N>


N>Идея очень проста: у каждого метода существует свой стек вычислений (evaluation stack, не путать со стеком вызовов методов!). Он устроен наподобие стека у советских программируемых калькуляторов: есть инструкции, которые кладут операнды на вершину стека, и есть инструкции, которые снимают один или несколько операндов оттуда, заменяя их результатом вычисления соответствующей операции.


N>В .NET 2.0 появилась возможность создавать временные динамические методы без создания динамической сборки с помощью класса DynamicMethod. Читайте раздел MSDN, посвященный Reflection.Emit. Успехов!

Огромное спасибо за информацию. Теперь есть ориентиры.
As long as there is life, there is hope
Re[2]: Reflection - динамическое создание кода
От: DemAS http://demas.me
Дата: 29.03.07 05:44
Оценка:
Здравствуйте, nikov, Вы писали:

N>Бывают ситуации, когда в момент написания программы для некоторых типов возможно описать только интерфейсы (или даже обобщенные интерфейсы, используя generics). Поведение, то есть логику методов, возможно будет определить лишь на этапе исполнения, например, на основе информации, считанной из базы данных.


А не являются ли такие случаи ошибками проектирования?
Опыт подсказывает мне, что поддержка "динамического кода" довольно затруднительна. Как минимум, его сложно (если вообще возможно) отлаживать.
... << RSDN@Home 1.2.0 alpha rev. 669>>
Re[3]: Reflection - динамическое создание кода
От: nikov США http://www.linkedin.com/in/nikov
Дата: 29.03.07 06:41
Оценка:
Здравствуйте, DemAS, Вы писали:

N>>Поведение, то есть логику методов, возможно будет определить лишь на этапе исполнения, например, на основе информации, считанной из базы данных.


DAS> А не являются ли такие случаи ошибками проектирования?

DAS> Опыт подсказывает мне, что поддержка "динамического кода" довольно затруднительна.
DAS> Как минимум, его сложно (если вообще возможно) отлаживать.

Ну, наверняка есть случаи, когда использование динамической генерации кода не оправдано и является ошибкой.
Это "тяжелая артиллерия", которая должна использоваться по строгим показаниям. Использование динамической генерации содержит многие потенциальные проблемы, связанные как с трудностью отладки, так и с возможностью внесения ошибок в динамически создаваемый код, которые нельзя отловить на этапе компиляции. В самый неожиданный момент можно получить System.InvalidProgramException без какой-либо внятной диагностики.
Однако, люди создают средства, позволяющие минимизировать такие риски и помогающие производить генерацию кода, увеличивая степень контроля на этапе компиляции. То есть, сам генератор написан так, что затруднительно написать код, вызывающий его, который скомпилируется успешно, однако потерпит неудачу в ран-тайме.
Из таких средств можно отметить BLToolkit и сборку System.Query
Автор: nikov
Дата: 26.01.07
(пока существующую в бета-версии).
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.