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
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.