Использование директив и атрибутов CIL

92

В данной статье мы рассмотрим реализацию специального пространства имен с набором всех базовых конструкций.

Добавление ссылок на внешние сборки в CIL

Создадим с помощью предпочитаемого редактора новый файл с расширением *.il. Первым шагом в любом CIL-проекте является перечисление внешних сборок, которые будут использоваться в текущей сборке. В рассматриваемом примере применяются только типы из сборки mscorlib.dll. Следовательно, потребуется добавить ссылку только на эту сборку. Для этого необходимо указать в новом файле директиву .assembly с уточняющим атрибутом external. При добавлении ссылок на сборки, обладающие строгими именами, наподобие mscorlib.dll, должны также указываться директивы .publickeytoken и .ver:

.assembly extern mscorlib
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89)
  .ver 4:0:0:0
}

Строго говоря, явно добавлять ссылку на сборку mscorlib.dll не обязательно, поскольку ilasm.exe добавит ее автоматически. Для всех остальных внешних библиотек .NET, требуемых в проекте CIL, обязательно должны быть предусмотрены соответствующие директивы .assembly extern.

Определение текущей сборки в CIL

Следующий шаг состоит в определении интересующей сборки с использованием директивы .assembly. В самом простом варианте при определении сборки можно указывать только дружественное имя соответствующего двоичного файла. Хотя это вполне допустимое определение новой сборки .NET, обычно внутри такого объявления размещаются дополнительные директивы. В рассматриваемом примере определение сборки необходимо снабдить номером версии 1.0.0.0 с помощью директивы .ver (обратите внимание, что числа в номере версии разделяются двоеточиями, а не точками, как в C#).

Из-за того, что UserInfoCIL (пример сборки) представляет собой однофайловую сборку, в конце определения нужно добавить одну директиву .module и указать в ней официальное имя двоичного файла .NET:

// Определение сборки
.assembly UserInfoCIL
{
   .ver 1:0:0:1
}
.module UserInfoCIL.dll

Помимо .assembly и .module, существуют и другие CIL-директивы, которые позволяют дополнительно уточнять общую структуру создаваемого двоичного файла .NET:

.mresources

Если сборка должна использовать внутренние ресурсы (такие как растровые изображения или таблицы строк), эта директива позволяет указать имя файла, в котором содержатся ресурсы, подлежащие включению в сборку.

.subsystem

Эта CIL-директива служит для указания предпочитаемого пользовательского интерфейса, внутри которого должна выполняться сборка. Например, значение 2 указывает, что сборка должна выполняться в приложении с графическим пользовательским интерфейсом, а значение 3 — в консольном приложении.

Определение пространств имен и типов классов в CIL

После определения внешнего вида и поведения сборки (и требуемых внешних ссылок) можно переходить к созданию пространства имен .NET (MyNamespace), используя директиву .namespace:

// Пространство имен
.namespace MyUI
{
}

Как и в C#, в языке CIL можно создавать корневые и вложенные пространства имен. Хотя корневое пространство имен в рассматриваемом примере, в принципе, не нужно.

Пустые пространства имен не особо полезны, поэтому давайте посмотрим, как в CIL определяются типы классов. Для определения нового типа класса в CIL применяется директива .class. Эта простая директива может сопровождаться многочисленными дополнительными атрибутами, позволяющими уточнить природу типа. Как и в C#, если базовый класс явно не указан, тип будет автоматически наследоваться от System.Object.

При создании типа класса, унаследованного не от System.Object, применяется атрибут extends. Для ссылки на тип, определенный внутри той же самой сборки, в CIL должно использоваться полностью квалифицированное имя (хотя если базовый тип находится в пределах той же сборки, допускается не указывать префикс, представляющий дружественное имя сборки).

// Пространство имен
.namespace MyUI
{
   .class public MyClass { }
   
   // Класс MyUserInfo наследуется от класса MyClass
   .class public MyUserInfo
                 extends MyUI.MyClass {}
}

Помимо атрибутов public и extends, определение класса CIL может иметь множество дополнительных спецификаторов, уточняющих видимость типа, компоновку полей и т.п:

Атрибут Описание
abstract,
sealed
Эти два атрибута могут быть присоединены к директиве .class для определения, соответственно, абстрактного или запечатанного класса
public, private,
nested assembly,
nested famandassem,
nested family,
nested famorassem,
nested public,
nested private
Эти атрибуты применяются для указания степени видимости типа. Как не трудно заметить, в CIL предлагается много других возможностей помимо тех, что доступны в C#. За дополнительными сведениями обращайтесь в документ ЕСМА 335
auto, sequential, explicit Эти атрибуты позволяют указать CLR-среде, как данные полей должны размещаться в памяти. Для типов классов подходящим является флаг auto, используемый по умолчанию. Его изменение может быть полезно в случае использования P/Invoke для обращения к неуправляемому коду на С
extends, implements Эти атрибуты позволяют определять базовый класс для типа (extends) и реализовать для него интерфейс (implements)
Пройди тесты
Лучший чат для C# программистов