Генерирование динамического компоновочного блока

Генерирование динамического компоновочного блока

Чтобы проиллюстрировать процесс определения компоновочного блока .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");

}