Реализация базовых конструкций в CIL

86

Определение и реализация интерфейсов в CIL

Как ни странно, но типы интерфейсов в CIL тоже определяются с использованием директивы .class. Однако за счет ее сопровождения атрибутом interface тип реализуется как один из типов интерфейсов CTS. После определения интерфейс может привязываться к какому-нибудь типу класса или структуры с применением атрибута implements.

.namespace MyUI
{
   // Объявление интерфейса
   .class public interface IMyInterface {}

   .class public MyClass { }
   
   // Класс MyUserInfo наследуется от класса MyClass
   // и реализует интерфейс IMyInterface
   .class public MyUserInfo
                 extends MyUI.MyClass
                 implements MyUI.IMyInterface{}
}

Интерфейсы могут выступать в роли базовых для других типов интерфейсов, позволяя строить иерархии интерфейсов. Вопреки возможным ожиданиям, однако, использовать атрибут extends для наследования интерфейса А от интерфейса В в CIL нельзя. Этот атрибут разрешено применять только для указания базового класса типа. Для расширения интерфейса должен использоваться атрибут implements.

Определение структур в CIL

Директива .class может использоваться для определения любой структуры CTS при условии расширения ее типом System.ValueType. В этом случае она должна обязательно сопровождаться атрибутом sealed (поскольку структуры никогда не могут выступать в роли базовых для других типов-значений). В случае несоблюдения этого требования ilasm.exe будет сообщать об ошибке на этапе компиляции.

// При определении структуры должен быть указан атрибут sealed
.class public sealed MyStruct
       extends [mscorlib]System.ValueType{}

В CIL также предусмотрен сокращенный вариант синтаксиса для определения типа структуры. В случае использования атрибута value новый тип будет автоматически наследоваться от типа [mscorlib]System.ValueType. Следовательно, тип MyStruct можно было бы определить и так:

// Сокращенный вариант объявления структуры
.class public value MyStruct{}

Определение перечислений в CIL

Перечисления .NET унаследованы от класса System.Enum, который, в свою очередь, наследуется от System.ValueType (и потому должен обязательно сопровождаться атрибутом sealed). Чтобы определить перечисление в CIL, необходимо расширить [mscorlib]System.Enum показанным ниже образом:

// Определение перечисления
.class public sealed MyEnum
       extends [mscorlib]System.Enum{}

Как и для структур, для перечислений тоже поддерживается сокращенный синтаксис определения, предусматривающий использование атрибута enum:

// Сокращенный вариант определения перечисления.
.class public sealed enum MyEnum{}

Делегаты, которые являются еще одним фундаментальным типом в .NET, тоже имеют свое специфическое представление в CIL.

Определение обобщений в CIL

Обобщенные типы также имеют свое представление в синтаксисе CIL. Вспомните, что каждый обобщенный тип или член может иметь один и более параметров типа. Например, тип List<T> обладает одним единственным параметром, а Dictionary<TKey, TValue> — двумя. В CIL количество параметров типа указывается с использованием символа обратной одиночной кавычки (`) со следующим за ним числом. Как и в C#, сами значения параметров типа заключаются в квадратные скобки.

Например, предположим, что необходимо создать переменную List<T>, где на месте T должно находиться значение типа System.Int32. В CIL это делается следующим образом (в области действия любого метода CIL):

// В C#: List<int> myInts = new List<int>();
newobj instance void class [mscorlib]
System.Collections.Generic.List`1<int32>::.ctor()

Обратите внимание, что данный обобщенный класс определен как List`1<int32>, поскольку List<T> имеет только один параметр типа.

Компиляция файла program.il

Итак, разрабатываемый файл program.il на данном этапе содержит следующий код:

.assembly extern mscorlib
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89)
  .ver 4:0:0:0
}
// Определение сборки
.assembly UserInfoCIL
{
   .ver 1:0:0:1
}
.module UserInfoCIL.dll

// Пространство имен
.namespace MyUI
{
   // Объявление интерфейса
   .class public interface IMyInterface {}

   .class public MyClass { }
   
   // Класс MyUserInfo наследуется от класса MyClass
   // и реализует интерфейс IMyInterface
   .class public MyUserInfo
                 extends MyUI.MyClass
                 implements MyUI.IMyInterface{}
}

Несмотря на то что ни члены, ни код реализации в определенные ранее типы не добавлялись, данный файл *.il уже можно компилировать в .NET-сборку с расширением .dll (это является единственным доступным вариантом, поскольку метод Main() не был определен). Для этого необходимо открыть окно командной строки и ввести следующую команду для запуска утилиты ilasm.ехе:

ilasm /dll program.il

После выполнения этой команды можно загрузить полученный двоичный файл в ildasm.exe и просмотреть его:

Содержимое сборки program.dll

Просмотрев содержимое сборки, можно запустить в отношении нее утилиту peverify.ехе. Обратите внимание, что это приведет к выдаче ряда сообщений об ошибках, так как все типы совершенно пусты.

Пройди тесты
Лучший чат для C# программистов