constructorIl.Emit(OpCodes.Stfld, msgField);
constructorIl.Emit(OpCodes.Ret);
<b> // Создать стандартный конструктор.</b>
helloWorldClass.DefineDefaultConstructor(
MethodAttributes.Public);
<b> // Создать метод GetMsg().</b>
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);
<b> // Создать метод SayHello().</b>
MethodBuilder sayHiMethod = helloWorldClass.DefineMethod(
"SayHello", MethodAttributes.Public, null, null);
methodIl = sayHiMethod.GetILGenerator();
methodIl.EmitWriteLine("Hello from the HelloWorld class!");
methodIl.Emit(OpCodes.Ret);
<b> // Выпустить класс HelloWorld.</b>
helloWorldClass.CreateType();
return builder;
}
Выпуск сборки и набора модулей
Тело метода начинается с установления минимального набора характеристик сборки с применением типов
AssemblyName
и
Version
(определенных в пространстве имен
System.Reflection
). Затем производится получение объекта типа
AssemblуBuilder
через статический метод
AssemblyBuilder.DefineDynamicAssembly()
.
При вызове метода
DefineDynamicAssembly()
должен быть указан режим доступа к определяемой сборке, наиболее распространенные значения которого представлены в табл. 19.9.
Следующая задача связана с определением набора модулей (и имени) для новой сборки. Метод
DefineDynamicModule()
возвращает ссылку на действительный объект типа
ModuleBuilder
:
// Создать новую сборку.
var builder = AssemblyBuilder.DefineDynamicAssembly(
assemblyName,AssemblyBuilderAccess.Run);
Роль типа ModuleBuilder
Тип
ModuleBuilder
играет ключевую роль во время разработки динамических сборок. Как и можно было ожидать,
ModuleBuilder
поддерживает несколько членов, которые позволяют определять набор типов, содержащихся внутри модуля (классы, интерфейсы, структуры и т.д.), а также набор встроенных ресурсов (таблицы строк, изображения и т.п.). В табл. 19.10 описаны два метода, относящиеся к созданию. (Обратите внимание, что каждый метод возвращает объект связанного типа, который представляет тип, подлежащий созданию.)
Основным членом класса
ModuleBuilder
является метод
DefineType()
. Кроме указания имени типа (в виде простой строки) с помощью перечисления
System.Reflection.TypeAttributes
можно описывать формат этого типа. В табл. 19.11 приведены избранные члены перечисления
TypeAttributes
.
Выпуск типа HelloClass и строковой переменной-члена
Теперь, когда вы лучше понимаете роль метода
ModuleBuilder.CreateType()
, давайте посмотрим, как можно выпустить открытый тип класса
HelloWorld
и закрытую строковую переменную:
<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);
Обратите внимание, что метод
TypeBuilder.DefineField()
предоставляет доступ к объекту типа
FieldBuilder
. В классе
TypeBuilder
также определены дополнительные методы, которые обеспечивают доступ к другим типам "построителей". Например, метод
DefineConstructor()
возвращает объект типа
ConstructorBuilder
, метод
DefineProperty()
— объект типа
PropertyBuilder
и т.д.
Выпуск конструкторов
Как упоминалось ранее, для определения конструктора текущего типа можно применять метод
TypeBuilder.DefineConstructor()
. Однако когда дело доходит до реализации конструктора
HelloClass
, в тело конструктора необходимо вставить низкоуровневый код CIL, который будет отвечать за присваивание входного параметра внутренней закрытой строке. Чтобы получить объект типа
ILGenerator
, понадобится вызвать метод
GetILGenerator()
из соответствующего типа "построителя" (в данном случае
ConstructorBuilder
).
Помещение кода CIL в реализацию членов осуществляется с помощью метода
Emit()
класса
ILGenerator
. В самом методе
Emit()
часто используется тип класса
Opcodes
, который открывает доступ к набору кодов операций CIL через свойства только для чтения. Например, свойство
Opcodes.Ret
обозначает возвращение из вызова метода
.Opcodes.Stfid
создает присваивание значения переменной-члену, a
Opcodes.Call
применяется для вызова заданного метода (конструктора базового класса в данном случае). Итак, логика для реализации конструктора будет выглядеть следующим образом: