Чтение онлайн

ЖАНРЫ

Язык программирования C#9 и платформа .NET5
Шрифт:

Выпуск конструкторов

Как упоминалось ранее, для определения конструктора текущего типа можно применять метод

TypeBuilder.DefineConstructor
. Однако когда дело доходит до реализации конструктора
HelloClass
, в тело конструктора необходимо вставить низкоуровневый код CIL, который будет отвечать за присваивание входного параметра внутренней закрытой строке. Чтобы получить объект типа
ILGenerator
, понадобится вызвать метод
GetILGenerator
из соответствующего типа "построителя" (в данном случае
ConstructorBuilder
).

Помещение

кода CIL в реализацию членов осуществляется с помощью метода
Emit
класса
ILGenerator
. В самом методе
Emit
часто используется тип класса
Opcodes
, который открывает доступ к набору кодов операций CIL через свойства только для чтения. Например, свойство
Opcodes.Ret
обозначает возвращение из вызова метода
.Opcodes.Stfid
создает присваивание значения переменной-члену, a
Opcodes.Call
применяется для вызова заданного метода (конструктора базового класса в данном случае). Итак, логика для реализации конструктора будет выглядеть следующим образом:

// Создать специальный конструктор, принимающий

// единственный аргумент типа string.

Type[] constructorArgs = new Type[1];

constructorArgs[0] = typeof(string);

ConstructorBuilder constructor =

helloWorldClass.DefineConstructor(

MethodAttributes.Public,

CallingConventions.Standard,

constructorArgs);

// Выпустить необходимый код CIL для конструктора.

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);

// Загрузить в стек указатель this объекта.

constructorIl.Emit(OpCodes.Ldarg_0);

constructorIl.Emit(OpCodes.Ldarg_1);

// Загрузить входной аргумент в виртуальный стек и сохранить его в msgField

constructorIl.Emit(OpCodes.Stfld, msgField);

constructorIl.Emit(OpCodes.Ret);

Как вам теперь уже известно, в результате определения специального конструктора для типа стандартный конструктор молча удаляется. Чтобы снова определить конструктор без аргументов, нужно просто вызвать метод

DefineDefaultConstructor
типа
TypeBuilder
:

// Создать стандартный конструктор.

helloWorldClass.DefineDefaultConstructor(

MethodAttributes.Public);

Выпуск

метода SayHello

В заключение давайте исследуем процесс выпуска метода

SayHello
. Первая задача связана с получением объекта типа
MethodBuilder
из переменной
helloWorldClass
. После этого можно определить сам метод и получить внутренний объект типа
ILGenerator
для вставки необходимых инструкций CIL:

// Создать метод SayHello.

MethodBuilder sayHiMethod = helloWorldClass.DefineMethod(

"SayHello", MethodAttributes.Public, null, null);

methodIl = sayHiMethod.GetILGenerator;

// Вывести строку на консоль.

methodIl.EmitWriteLine("Hello from the HelloWorld class!");

methodIl.Emit(OpCodes.Ret);

Здесь был определен открытый метод (т.к. указано значение

MethodAttributes.Public
), который не имеет параметров и ничего не возвращает (на что указывают значения
null
в вызове
DefineMethod
). Также обратите внимание на вызов
EmitWriteLine
. Посредством данного вспомогательного метода класса
ILGenerator
можно записать строку в стандартный поток вывода, приложив минимальные усилия.

Использование динамически сгенерированной сборки

Теперь, когда у вас есть логика для создания сборки, осталось лишь выполнить сгенерированный код. Логика в вызывающем коде обращается к методу

CreateMyAsm
, получая ссылку на созданный объект
AssemblyBuilder.

Далее вы поупражняетесь с поздним связыванием (см. главу 17) для создания экземпляра класса

HelloWorld
и взаимодействия с его членами. Модифицируйте операторы верхнего уровня, как показано ниже:

using System;

using System.Reflection;

using System.Reflection.Emit;

Console.WriteLine("***** The Amazing Dynamic Assembly Builder App *****");

// Создать объект AssemblyBuilder с использованием вспомогательной функции.

AssemblyBuilder builder = CreateMyAsm;

// Получить тип HelloWorld.

Type hello = builder.GetType("MyAssembly.HelloWorld");

// Создать экземпляр HelloWorld и вызвать корректный конструктор.

Console.Write("-> Enter message to pass HelloWorld class: ");

string msg = Console.ReadLine;

object[] ctorArgs = new object[1];

ctorArgs[0] = msg;

object obj = Activator.CreateInstance(hello, ctorArgs);

// Вызвать метод SayHelloO и отобразить возвращенную строку.

Поделиться с друзьями: