При динамическом создании перечисления мы действуем аналогично созданию классов. Только вместо класса TypeBuilder мы используем для создания перечисления класс EnumBuilder, в который потом добавляем нужные члены вместе с числовыми значениями.
using System;
using System.Reflection;
using System.Reflection.Emit;
// Создание имени сборки
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");
// Создание типа в сборке
EnumBuilder tb = mb.DefineEnum("MyColors", TypeAttributes.Public, typeof(int));
// Добавление членов в перечисление
tb.DefineLiteral("Red", 0);
tb.DefineLiteral("Pink", 1);
tb.DefineLiteral("Green", 2);
// Непосредственное создание типа
tb.CreateType();
// Сохранение типа в файл
ab.Save("My2.dll");
После выполнения этого кода в папке вашего проекта образуется файл My2.dll, внутри которого будет перечисление enum с именем MyColors, содержащий значения Red, Pink и Green.
2.2.7.4 Динамический "Hello World!"
Рассмотрим пример обучения программ программированию на примере классической программы "Hello World!".
Динамический вариант "Hello World!":
using System;
using System.Reflection;
using System.Reflection.Emit;
namespace DynHelloWorld
{ class Programmer
{ static public Type WriteCode()
{ AssemblyName assemblyName = new AssemblyName();
assemblyName.Name = "HelloWorldAssembly";
AssemblyBuilder assemblyBuilder =
AppDomain.CurrentDomain.DefineDynamicAssembly(
assemblyName, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder =
assemblyBuilder.DefineDynamicModule("HelloWorldModule");
TypeBuilder typeBuilder =
moduleBuilder.DefineType("HelloWorldClass"
, TypeAttributes.Public);
MethodBuilder methodBuilder =
typeBuilder.DefineMethod("HelloWorld"
, MethodAttributes.Public
, null, null);
ILGenerator il = methodBuilder.GetILGenerator();
// Генерируем код.
il.EmitWriteLine("Hello World!");
il.Emit(OpCodes.Ret);
return typeBuilder.CreateType();
}
}
class Class1
{ static void Main()
{ Type typeCode = Programmer.WriteCode();
object objCode = Activator.CreateInstance(typeCode);
MethodInfo methodInfo = typeCode.GetMethod("HelloWorld");
methodInfo.Invoke(objCode, null);
Console.ReadLine();
}
}
}
Класс-программист Programmer пишет код методом WriteCode(). Код – это метод класса HelloWorldClass, который содержится в модуле HelloWorldModule, который принадлежит сборке HelloWorldAssembly.
Класс-программист создаёт эти объекты с помощью набора соответствующих объектов-Buider'oв, прописанных в пространстве имён System. Reflection. Emit, попутно задавая атрибуты создаваемых объектов. В данном случае и тип и метод создаются как открытые (об этом говорят флаги TypeAttributes. Public и MethodAttributes. Public).
Самое интересное, конечно – это непосредственное генерирование кода. Он в данном случае состоит всего из двух команд языка IL: вывод строки на консоль и возврат из процедуры.
Если динамически сгенерированный класс широко используется в программе, то было бы удобно использовать его точно так же, как классы в сборках, создаваемых и используемых обычным образом.
Но для этого надо обеспечить, чтобы сборка генерировалась при первой необходимости. О потребности в сборке можно узнать, перехватив событие AssemblyResolve класса AppDomain. Это событие генерируется при любой неудачной попытке загрузить в домен какую-либо сборку. А так как нашей сборки ещё нет (она ещё не сгенерирована), то любая попытка её загрузить будет неудачной.