Литмир - Электронная Библиотека
A
A

  constructorIl.Emit(OpCodes.Stfld, msgField);

  constructorIl.Emit(OpCodes.Ret);

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

  helloWorldClass.DefineDefaultConstructor(

    MethodAttributes.Public);

<b>  // Создать метод GetMsg().</b>

  MethodBuilder getMsgMethod = helloWorldClass.DefineMethod(

    &quot;GetMsg&quot;,

    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(

    &quot;SayHello&quot;, MethodAttributes.Public, null, null);

  methodIl = sayHiMethod.GetILGenerator();

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

  methodIl.Emit(OpCodes.Ret);

<b>  // Выпустить класс HelloWorld.</b>

  helloWorldClass.CreateType();

  return builder;

}

Выпуск сборки и набора модулей

Тело метода начинается с установления минимального набора характеристик сборки с применением типов

AssemblyName
и
Version
(определенных в пространстве имен
System.Reflection
). Затем производится получение объекта типа
AssemblуBuilder
через статический метод
AssemblyBuilder.DefineDynamicAssembly()
.

При вызове метода

DefineDynamicAssembly()
должен быть указан режим доступа к определяемой сборке, наиболее распространенные значения которого представлены в табл. 19.9.

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

Следующая задача связана с определением набора модулей (и имени) для новой сборки. Метод

DefineDynamicModule()
возвращает ссылку на действительный объект типа
ModuleBuilder
:

 // Создать новую сборку.

  var builder = AssemblyBuilder.DefineDynamicAssembly(

    assemblyName,AssemblyBuilderAccess.Run);

Роль типа ModuleBuilder

 Тип

ModuleBuilder
играет ключевую роль во время разработки динамических сборок. Как и можно было ожидать,
ModuleBuilder
поддерживает несколько членов, которые позволяют определять набор типов, содержащихся внутри модуля (классы, интерфейсы, структуры и т.д.), а также набор встроенных ресурсов (таблицы строк, изображения и т.п.). В табл. 19.10 описаны два метода, относящиеся к созданию. (Обратите внимание, что каждый метод возвращает объект связанного типа, который представляет тип, подлежащий созданию.)

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

Основным членом класса

ModuleBuilder
является метод
DefineType()
. Кроме указания имени типа (в виде простой строки) с помощью перечисления
System.Reflection.TypeAttributes
можно описывать формат этого типа. В табл. 19.11 приведены избранные члены перечисления
TypeAttributes
.

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

Выпуск типа HelloClass и строковой переменной-члена

Теперь, когда вы лучше понимаете роль метода

ModuleBuilder.CreateType()
, давайте посмотрим, как можно выпустить открытый тип класса
HelloWorld
и закрытую строковую переменную:

<b>// Определить открытый класс по имени HelloWorld.</b>

TypeBuilder helloWorldClass =

  module.DefineType(&quot;MyAssembly.HelloWorld&quot;,

  TypeAttributes.Public);

<b>// Определить закрытую переменную-член типа String по имени theMessage.</b>

FieldBuilder msgField = helloWorldClass.DefineField(

  &quot;theMessage&quot;,

  Type.GetType(&quot;System.String&quot;),

  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
применяется для вызова заданного метода (конструктора базового класса в данном случае). Итак, логика для реализации конструктора будет выглядеть следующим образом:

358
{"b":"847442","o":1}