ConstructorBuilder myCtorBuilder = /* */;
ILGenerator myCILGen = myCtorBuilder.GetILGenerator();
Имея объект
ILGenerator
, с помощью его методов можно выпускать низкоуровневые коды операций CIL. Некоторые (но не все) методы
ILGenerator
кратко описаны в табл. 19.8.
Основным методом класса
ILGenerator
является
Emit()
, который работает в сочетании с типом
System.Reflection.Emit.Opcodes
. Как упоминалось ранее в главе, данный тип открывает доступ к множеству полей только для чтения, которые отображаются на низкоуровневые коды операций CIL. Полный набор этих членов документирован в онлайновой справочной системе, и далее в главе вы неоднократно встретите примеры их использования.
Выпуск динамической сборки
Чтобы проиллюстрировать процесс определения сборки .NET Core во время выполнения, давайте рассмотрим процесс создания однофайловой динамической сборки по имени
MyAssembly.dll
.
Внутри модуля находится класс
HelloWorld
, который поддерживает стандартный конструктор и специальный конструктор, применяемый для присваивания значения закрытой переменной-члена (
theMessage
) типа
string
. Вдобавок в классе
HelloWorld
имеется открытый метод экземпляра под названием
SayНеllo()
, который выводит приветственное сообщение в стандартный поток ввода-вывода, и еще один метод экземпляра по имени
GetMsg()
, возвращающий внутреннюю закрытую строку. По существу мы собираемся программно сгенерировать следующий тип класса:
<b>// Этот класс будет создаваться во время выполнения с использованием</b>
<b>// пространства имен System.Reflection.Emit.</b>
public class HelloWorld
{
private string theMessage;
HelloWorld() {}
HelloWorld(string s) {theMessage = s;}
public string GetMsg() {return theMessage;}
public void SayHello()
{
System.Console.WriteLine("Hello from the HelloWorld class!");
}
}
Создайте новый проект консольного приложения по имени
MyAsmBuilder
и добавьте NuGet-пакет
System.Reflection.Emit
. Импортируйте в него пространства имен
System.Reflection
и
System.Reflection.Emit
. Определите в классе
Program
статический метод по имени
CreateMyAsm()
. Этот единственный метод будет отвечать за решение следующих задач:
• определение характеристик динамической сборки (имя, версия и т.п.);
• реализация типа
HelloClass
;
• возвращение вызывающему методу объекта
AssemblyBuilder
.
Ниже приведен полный код, а затем его анализ:
static AssemblyBuilder CreateMyAsm()
{
<b> // Установить общие характеристики сборки.</b>
AssemblyName assemblyName = new AssemblyName
{
Name = "MyAssembly",
Version = new Version("1.0.0.0")
};
<b> // Создать новую сборку.</b>
var builder = AssemblyBuilder.DefineDynamicAssembly(
assemblyName,AssemblyBuilderAccess.Run);
<b> // Определить имя модуля.</b>
ModuleBuilder module =
builder.DefineDynamicModule("MyAssembly");
<b> // Определить открытый класс по имени HelloWorld.</b>
TypeBuilder helloWorldClass =
module.DefineType("MyAssembly.HelloWorld",
TypeAttributes.Public);
<b> // Определить закрытую переменную-член типа String по имени theMessage.</b>
FieldBuilder msgField = helloWorldClass.DefineField(
"theMessage",
Type.GetType("System.String"),
attributes: FieldAttributes.Private);
<b> // Создать специальный конструктор.</b>
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);