в каталог проекта. Скомпилируйте сборку с применением .NET Core CLI:
dotnet build
Результирующие файлы будут находиться, как обычно, в подкаталоге
bin\debug\net5.0
. На этом этапе новое приложение можно запустить. Разумеется, в окне консоли отобразится обновленное сообщение. Хотя приведенный простой пример не является особенно впечатляющим, он иллюстрирует один из сценариев применения возвратного проектирования на CIL.
Директивы и атрибуты CIL
Теперь, когда вы знаете, как преобразовывать сборки .NET Core в файлы
*.il
и компилировать файлы
*.il
в сборки, можете переходить к более детальному исследованию синтаксиса и семантики языка CIL. В последующих разделах будет поэтапно рассматриваться процесс создания специального пространства имен, содержащего набор типов. Тем не менее, для простоты типы пока не будут иметь логики реализации своих членов. Разобравшись с созданием простых типов, можете переключить внимание на процесс определения "реальных" членов с использованием кодов операций CIL.
Указание ссылок на внешние сборки в CIL
Скопируйте файлы
global.json
и
NuGet.config
из предыдущего примера в новый каталог проекта. Создайте новый файл проекта по имени
Затем создайте в текстовом редакторе новый файл по имени
CILTypes.il
. Первой задачей в проекте CIL является перечисление внешних сборок, которые будут задействованы текущей сборкой. В рассматриваемом примере применяются только типы, находящиеся внутри сборки
System.Runtime.dll
. В новом файле понадобится указать директиву
.assembly
с уточняющим атрибутом
external
. При добавлении ссылки на сборку со строгим именем, подобную
System.Runtime.dll
, также должны быть указаны директивы
.publickeytoken
и
.ver
:
.assembly extern System.Runtime
{
.publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A )
.ver 5:0:0:0
}
Определение
текущей сборки в CIL
Следующее действие заключается в определении создаваемой сборки с использованием директивы
.assembly
. В простейшем случае сборка может быть определена за счет указания дружественного имени двоичного файла:
// Наша сборка.
.assembly CILTypes{}
Хотя такой код действительно определяет новую сборку .NET Core, обычно внутрь объявления будут помещаться дополнительные директивы. В рассматриваемом примере определение сборки необходимо снабдить номером версии 1.0.0.0 посредством директивы
.ver
(обратите внимание, что числа в номере версии отделяются друг от друга двоеточиями, а не точками, как принято в С#):
// Наша сборка.
.assembly CILTypes
{
.ver 1:0:0:0
}
Из-за того, что сборка
CILTypes
является однофайловой, ее определение завершается с применением следующей директивы
.module
, которая обозначает официальное имя двоичного файла .NET Core, т.е.
CILTypes.dll
:
// Наша сборка
.assembly CILTypes
{
.ver 1:0:0:0
}
// Модуль нашей однофайловой сборки.
.module CILTypes.dll
Кроме
.assembly
и
.module
существуют директивы CIL, которые позволяют дополнительно уточнять общую структуру создаваемого двоичного файла .NET Core. В табл. 19.1 перечислены некоторые наиболее распространенные директивы уровня сборки.
Определение пространств имен в CIL
Определив внешний вид и поведение сборки (а также обязательные внешние ссылки), вы можете создать пространство имен .NET Core (
MyNamespace
), используя директиву
.namespace
:
// Наша сборка имеет единственное пространство имен.
.namespace MyNamespace {}
Подобно C# определения пространств имен CIL могут быть вложены в другие пространства имен. Хотя здесь нет нужды определять корневое пространство имен, ради интереса посмотрим, как создать корневое пространство имен
MyCompany
:
.namespace MyCompany
{
.namespace MyNamespace {}
}
Как и С#, язык CIL позволяет определить вложенное пространство имен следующим образом: