Генерирование динамического компоновочного блока
Генерирование динамического компоновочного блока
Чтобы проиллюстрировать процесс определения компоновочного блока .NET в среде выполнения, давайте создадим одномодульный динамический компоновочный блок с именем MyAssembly.dll. В этом модуле будет содержаться класс HelloWorld. Тип HelloWorld поддерживает конструктор, используемый по умолчанию, и пользовательский конструктор для присваивания значения приватной переменной (theMessage) типа string. Кроме того, HelloWorld предлагает открытый метод экземпляра с именем SayHello(), который выводит приветствие в стандартный поток ввода-вывода, а также еще один метод экземпляра, GetMsg(), который возвращает внутреннюю приватную строку. В результате вы должны программно сгенерировать следующий тип класса.
// Этот класс будет создан в среде выполнения
// с помощью System.Reflection.Emit.
public class HelloWorld {
private string theMessage;
HelloWorld() {}
HelloWorld(string s) { theMessage = s; }
public string GetMsg() { return theMessage; }
public void SayHello() {
System.Console.WriteLine("Привет от класса HelloWorld!");
}
}
Предположим, вы cоздали новый проект консольного приложения в Visual Studio 2005, назвав его DynAsmBuilder. Переименуйте исходный класс в MyAsmBuilder и определите статический метод с именем CreateMyAsm(). Этот единственный метод будет ответственен за следующее:
• определение характеристик динамического компоновочного блока (имя, версия и т.д.);
• реализацию тина HelloClass;
• запись компоновочного блока, сгенерированного в памяти, в физический файл.
Также отметим, что метод CreateMyAsm() использует в качестве единственного параметра тип System.AppDomain, который будет использоваться для получения доступа к типу AssemblyBuilder, связанному с текущим доменом приложения (см. главу 13, где обсуждаются домены приложений .NET). Вот полный программный код, с последующим анализом.
// Вызывающая сторона посылает тип AppDomain.
public static void CreateMyAsm(AppDomain currAppDomain) {
// Установка общих характеристик компоновочного блока.
AssemblyName assemblyName = new AssemblyName();
assemblyName.Name = "MyAssembly";
assemblyName.Version = new Version("1.0.0.0");
// Создание нового компоновочного блока
// в рамках текущего домена приложения.
AssemblyBuilder assembly = curAppDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Save);
// Поскольку создается одномодульный компоновочный блок,
// имя модуля будет совпадать с именем компоновочного блока.
ModuleBuilder module = assembly.DefineDynamicModule("MyAssembly", "MyAssemblу.dll");
// Определение открытого класса с именем "HelloWorld".
TypeBuilder helloWorldClass = module.DefineType("MyAssembly.HelloWorld", TypeAttributes.Public);
// Определение приватной переменной String с именем "theMessage".
FieldBuilder msgField = helloWorldClass.DefineField("theMessage", Type.GetType("System.String"), FieldAttributes.Private);
// Создание пользовательского конструктора.
Type[] constructorArgs = new Type[1];
constructorArgs[0] = typeof(string);
ConstructorBuilder constructor = helloWorldClass.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, constructorArgs);
ILGenerator constructorIL = constructor.GetILGenerator();
constructorIL.Emit(OpCodes.Ldarg_0);
Type objectClass = typeof(object);
ConstructorInfo superConstructor = objectClass.GetConstructor(new Type[0]);
constructorIL.Emit(OpCodes.Call, superConstructor);
constructorIL.Emit(Opcodes.Ldarg_0);
constructorIL.Emit(Opcodes.Ldarg_1);
constructorIL.Emit(OpCodes.Stfld, msgField);
constructorIL.Emit(OpCodes.Ret);
// Создание конструктора, заданного по умолчанию.
helloWorldClass.DefineDefaultConstructor(MethodAttributes.Public);
// Теперь создание метода GetMsg().
MethodBuilder getMsgMethod = helloWorldClass.DefineMethod("GetMsg", MethodAttributes.Public, typeof(string), null);
ILGenerator methodIL = getMsgMethod.GetILGenerator();
methodIL.Emit(OpCodes.Ldarg_0);
methodIL.Emit(OpCodes.Ldfld, msgField);
methodIL.Emit(Opcodes.Ret);
// Создание метода SayHello.
MethodBuilder sayHiMethod = helloWorldClass.DefineMethod("SayHello", MethodAttributes.Public, null, null);
methodIL = sayHiMethod.GetILGenerator();
methodIL.EmitWriteLine("Привет от класса HelloWorld!");
methodIL.Emit(Opcodes.Ret);
// Генерирование класса HelloWorld.
helloWorldClass.CreateType();
// (Необязательно.) Сохранение компоновочного блока в файл.
assembly.Save("MyAssembly.dll");
}