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

ЖАНРЫ

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

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

• Вы строите инструмент программирования .NET Core, который должен быть способным генерировать сборки по требованию на основе пользовательского ввода.

• Вы создаете приложение, которое нуждается в генерации посредников для удаленных типов на лету, основываясь на полученных метаданных.

• Вам необходима возможность загрузки статической сборки и динамической вставки в двоичный образ новых типов.

Давайте посмотрим, какие типы доступны в пространстве имен

System.Reflection.Emit
.

Исследование

пространства имен System.Reflection.Emit

Создание динамической сборки требует некоторых знаний кодов операций CIL, но типы из пространства имен

System.Reflection.Emit
максимально возможно скрывают сложность языка CIL. Скажем, вместо указания необходимых директив и атрибутов CIL для определения типа класса можно просто применять класс
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(

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