Основы C# Урок 72. Reflection - динамическое создание кода
Иногда нам требуется динамически создать некий код. Естественно, что этот код у нас будет храниться в некоторой сборке. При этом сама созданная динамически сборка может существовать только в памяти или же может быть сохраненной на диск в виде файла. Сейчас мы посмотрим, как это можно сделать.
Для начала небольшое замечание по порядку создания соответствующих объектов. Сначала мы должны сгенерировать сборку, затем на основании этой сборки - модуль, потом на основании этого модуля - тип (например, класс), потом на основании этого типа (класса) - его члены (конструкторы, методы и т. п.). И, уже в самом конце, мы создаем непосредственно сгенерированный на предыдущих шагах тип.
Вот пример такого кода:
// Создание имени сборки.
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");
Приведенный код при запуске создаст на жестком диске файл qqq.dll, в котором будет класс с 2-я конструкторами, причем второй конструктор будет при вызове выводить строчку "Constructor".
Несколько пояснений по коду.
Первое. Очень часто при создании типов и членов этих типов надо указать их атрибуты (модификаторы доступа типа public и т. п.). Это мы делаем через перечисления TypeAttributes и MethodAttributes, которое содержит соотвествующие значения (Public, например). Несколько необходимых значений из этих перечислений можно соединить через побитовое "или".
Второе. Метод Emit класса ILGenerator в качестве параметра принимает перечисление OpCodes, которое фактически содержит инструкции на языке IL - языке, который является аналогом для .NET обычного ассемблера. Это означает, что его инструкции не столь очевидны для реального программирования - именно поэтому в качестве примера таких IL-инструкций и была приведена самая простая из них - а именно выход из функции (OpCodes.Ret).
Parking.ru. Качественный виртуальный хостинг на платформе Windows(r): поддержка NET, многофункциональная панель управления, аренда бизнес-приложений, сертифицированные специалисты. Управляемый выделенный хостинг на платформе Windows(r): производительные серверы, профессиональная поддержка, аренда ПО Microsoft(r), безопасность, гарантии. http://www.parking.ru