Пустые пространства имен не особо интересны, поэтому давайте рассмотрим процесс определения типов классов в CIL. Для определения нового типа класса предназначена директива
.class
. Тем не менее, эта простая директива может быть декорирована многочисленными дополнительными атрибутами, уточняющими природу типа. В целях иллюстрации добавим в наше пространство имен открытый класс под названием
MyBaseClass
. Как и в С#, если базовый класс явно не указан, то тип автоматически становится производным от
System.Object
:
.namespace MyNamespace
{
// Предполагается базовый
класс System.Object.
.class public MyBaseClass {}
}
При построении типа, производного не от класса
System.Object
, применяется атрибут
extends
. Для ссылки на тип, определенный внутри той же самой сборки, язык CIL требует использования полностью заданного имени (однако если базовый тип находится внутри той же самой сборки, то префикс в виде дружественного имени сборки можно не указывать). Следовательно, демонстрируемая ниже попытка расширения
MyBaseClass
в результате дает ошибку на этапе компиляции:
// Этот код не скомпилируете»!
.namespace MyNamespace
{
.class public MyBaseClass {}
.class public MyDerivedClass
extends MyBaseClass {}
}
Чтобы корректно определить родительский класс для
MyDerivedClass
, потребуется указать полностью заданное имя
MyBaseClass
:
// Уже лучше!
.namespace MyNamespace
{
.class public MyBaseClass {}
.class public MyDerivedClass
extends MyNamespace.MyBaseClass {}
}
В дополнение к атрибутам
public
и
extends
определение класса CIL может иметь множество добавочных квалификаторов, которые управляют видимостью типа, компоновкой полей и т.д. В табл. 19.2 описаны избранные атрибуты, которые могут применяться в сочетании с директивой
.class
.
Определение и реализация интерфейсов в CIL
Несколько странно, но типы интерфейсов в CIL определяются с применением директивы
.class
. Тем не менее, когда директива
.class
декорирована атрибутом interface, тип трактуется как интерфейсный тип CTS. После определения интерфейс можно привязывать к типу класса или структуры с использованием атрибута
implements
:
.namespace MyNamespace
{
// Определение интерфейса.
.class public interface IMyInterface {}
// Простой базовый класс.
.class public MyBaseClass {}
// Теперь MyDerivedClass реализует IMylnterface
// и расширяет MyBaseClass.
.class public MyDerivedClass
extends MyNamespace.MyBaseClass
implements MyNamespace.IMyInterface {}
}
На
заметку! Конструкция
extends
должна предшествовать конструкции
implements
. Кроме того, в конструкции
implements
может содержаться список интерфейсов с разделителями-запятыми
Вспомните из главы 8, что интерфейсы могут выступать в роли базовых для других типов интерфейсов, позволяя строить иерархии интерфейсов. Однако вопреки возможным ожиданиям применять атрибут
extends
для порождения интерфейса
А
от интерфейса
В
в CIL нельзя. Атрибут
extends
используется только для указания базового класса типа. Когда интерфейс необходимо расширить, снова будет применяться атрибут
implements
, например:
// Расширение интерфейсов в CIL.
.class public interface IMyInterface {}
.class public interface IMyOtherInterface
implements MyNamespace.IMyInterface {}
Определение структур в CIL
Директива
.class
может использоваться для определения любой структуры CTS, если тип расширяет
System.ValueType
. Кроме того, такая директива
.class
должна уточняться атрибутом
sealed
(учитывая, что структуры никогда не могут выступать в роли базовых для других типов значений). Если попытаться поступить иначе, тогда компилятор
ilasm.exe
выдаст сообщение об ошибке.
// Определение структуры всегда является запечатанным.
.class public sealed MyStruct
extends [System.Runtime]System.ValueType{}
Имейте в виду, что в CIL предусмотрен сокращенный синтаксис для определения типа структуры. В случае применения атрибута
value
новый тип автоматически становится производным от
[System.Runtime]System.ValueType
. Следовательно, тип
MyStruct
можно было бы определить и так:
// Сокращенный синтаксис объявления структуры.
.class public sealed value MyStruct{}
Определение перечислений в CIL
Перечисления .NET Core порождены от класса
System.Enum
, который является
System.ValueType
(и потому также должен быть запечатанным). Чтобы определить перечисление в CIL, необходимо просто расширить
[System.Runtime]System.Enum
:
// Перечисление.
.class public sealed MyEnum
extends [System.Runtime]System.Enum{}
Подобно структурам перечисления могут быть определены с помощью сокращенного синтаксиса, используя атрибут
enum
:
// Сокращенный синтаксис определения перечисления.
.class public sealed enum MyEnum{}
Вскоре вы увидите, как указывать пары "имя-значение" перечисления.