Книги онлайн и без регистрации » Разная литература » Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен

Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 232 233 234 235 236 237 238 239 240 ... 407
Перейти на страницу:
типа класса можно просто применять класс TypeBuilder. Аналогично, если нужно определить новый конструктор уровня экземпляра, то не придется задавать лексему specialname, rtspecialname или .ctor; взамен можно использовать класс ConstructorBuilder. Основные члены пространства имен System.Reflection.Emit описаны в табл. 19.7.

В целом типы из пространства имен System.Reflection.Emit позволяют представлять низкоуровневые лексемы CIL программным образом во время построения динамической сборки. Вы увидите многие из них в рассматриваемом далее примере; тем не менее, тип ILGenerator заслуживает специального внимания.

Роль типа System.Reflection.Emit.ILGenerator

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

Вот пример:

// Получить объект ILGenerator из объекта ConstructorBuilder

// по имени myCtorBuilder.

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

// Этот класс будет создаваться во время выполнения с использованием

// пространства имен 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("Hello from the HelloWorld class!");

  }

}

Создайте новый проект консольного приложения по имени MyAsmBuilder и добавьте NuGet-пакет System.Reflection.Emit. Импортируйте в него пространства имен System.Reflection и System.Reflection.Emit. Определите в классе Program статический метод по имени CreateMyAsm(). Этот единственный метод будет отвечать за решение следующих задач:

• определение характеристик динамической сборки (имя, версия и т.п.);

• реализация типа HelloClass;

• возвращение вызывающему методу объекта AssemblyBuilder.

Ниже приведен полный код, а затем его анализ:

static AssemblyBuilder CreateMyAsm()

{

  // Установить общие характеристики сборки.

  AssemblyName assemblyName = new AssemblyName

  {

    Name = "MyAssembly",

    Version = new Version("1.0.0.0")

  };

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

  var builder = AssemblyBuilder.DefineDynamicAssembly(

    assemblyName,AssemblyBuilderAccess.Run);

  // Определить имя модуля.

  ModuleBuilder module =

    builder.DefineDynamicModule("MyAssembly");

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

  TypeBuilder helloWorldClass =

    module.DefineType("MyAssembly.HelloWorld",

    TypeAttributes.Public);

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

  FieldBuilder msgField = helloWorldClass.DefineField(

    "theMessage",

    Type.GetType("System.String"),

    attributes: 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("Hello from the HelloWorld class!");

  methodIl.Emit(OpCodes.Ret);

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

  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 описаны два метода, относящиеся к созданию. (Обратите внимание, что каждый метод возвращает объект связанного типа, который представляет тип, подлежащий созданию.)

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

1 ... 232 233 234 235 236 237 238 239 240 ... 407
Перейти на страницу:

Комментарии
Минимальная длина комментария - 20 знаков. В коментария нецензурная лексика и оскорбления ЗАПРЕЩЕНЫ! Уважайте себя и других!
Комментариев еще нет. Хотите быть первым?