Определение членов типов CIL
69C# --- Сборки .NET --- Определение членов типов CIL
Как уже известно, типы в .NET могут иметь различные члены. Так, перечисления могут иметь набор пар имен и значений, а структуры и классы — конструкторы, поля, методы, свойства, статические члены и т.д.
Определение полей данных в CIL
Перечисления, структуры и классы поддерживают поля данных. В каждом случае для их определения должна использоваться директива .field. Например, добавим в перечисление MyEnum три пары имен и значений (обратите внимание, что значения указываются в круглых скобках):
.class public sealed enum MyEnum{
.field public static literal valuetype
MyUI.MyEnum A = int32(0)
.field public static literal valuetype
MyUI.MyEnum B = int32(1)
.field public static literal valuetype
MyUI.MyEnum C = int32(2)
}
Здесь видно, что поля, размещаемые в рамках любого типа, который наследуется от System.Enum, квалифицируются атрибутами static и literal. Как не трудно догадаться, эти атрибуты указывают, что данные этих полей должны представлять собой фиксированное значение, доступное только из самого типа (например, MyEnum.А). Значения, присваиваемые полям в перечислении, могут быть предоставлены в шестнадцатеричном формате, и в этом случае сопровождаться префиксом Ох.
Конечно, если нужно определить какое-то поле данных внутри класса или структуры, использовать только общедоступные статические литеральные данные вовсе не обязательно. Как и в C#, поля данных в классе будут автоматически инициализироваться соответствующими значениями по умолчанию. Чтобы предоставить пользователю объекта возможность задавать свои значения во время создания приватных полей данных, потребуется создать специальные конструкторы.
Определение конструкторов для типов в CIL
В CTS поддерживается создание конструкторов, действующих как на уровне всего экземпляра, так и на уровне конкретного класса (статических конструкторов). В CIL первые представляются с помощью лексемы .ctor, а вторые — посредством лексемы .cctor (class constructor — конструктор класса). Обе лексемы в CIL должны обязательно сопровождаться атрибутами rtspecialname (return type special name — специальное имя возвращаемого типа) и specialname. Попросту говоря, эти атрибуты применяются для обозначения специфической лексемы CIL, которая может интерпретироваться уникальным образом в любом отдельно взятом языке .NET. Например, в C# возвращаемый тип в конструкторах не определяется, но в CIL оказывается, что возвращаемым значением конструктора на самом деле является void:
.class public MyUserInfo
extends MyUI.MyClass
implements MyUI.IMyInterface
{
// Определяем несколько полей в классе
.field private string stringField
.field private int32 intField
// Определяем конструктор
.method public hidebysig specialname rtspecialname
instance void .ctor(string s, int32 i) cil managed
{
// Какой-то код реализации
}
}
Обратите внимание, что здесь директива .ctor снабжена атрибутом instance (поскольку в данном случае конструктор не является статическим). Атрибуты cil managed указывают на то, что в рамках данного метода содержится код CIL, а не неуправляемый код, который может использоваться при выполнении запросов на вызов платформы.
Определение свойств в CIL
Свойства и методы тоже имеют специальный формат представления в CIL. Например, чтобы модифицировать класс MyUserInfo с целью поддержки общедоступного свойства по имени TheString, можно написать следующий CIL-код (обратите внимание на использование атрибута specialname):
// Свойство TheString
.method public hidebysig specialname
instance string get_TheString() cil managed
{
// Код реализации
}
.method public hidebysig specialname
instance void set_TheString(string 'value') cil managed
{
// Какой-то код
}
.property instance string TheString()
{
.get instance string
MyUI.MyUserInfo::get_TheString()
.set instance void
MyUI.MyUserInfo::set_TheString(string)
}
Вспомните, что в CIL каждое свойство отображается на пару методов, снабженных префиксами get_ и set_. В директиве .property используются соответствующие директивы .get и .set для отображения синтаксиса свойств на корректные "специально именованные" методы.
Входной параметр, передаваемый методу set в определении свойства, заключен в одинарные кавычки и представляет собой имя лексемы, которая должна использоваться в правой части операции присваивания в рамках метода.
Определение параметров членов
В целом аргументы в CIL задаются (более или менее) тем же образом, что и в C#. Например, каждый аргумент в CIL определяется за счет указания типа данных, к которому он относится, и затем самого его имени. Более того, как и в C#, в CIL можно определять входные, выходные и передаваемые по ссылке параметры. В CIL допускается определять аргументы, представляющие массивы параметров (равнозначно тому, что в C# делается с помощью ключевого слова params), и необязательные параметры (которые в C# не поддерживаются, но зато поддерживаются в VB).
Рассмотрим процесс определения параметров в CIL на примере. Предположим, что требуется создать метод, принимающий один параметр int32 (по значению), второй параметр int32 (по ссылке), параметр [mscorlib] System.Collection.ArrayList и еще один выходной параметр (тоже типа int32). В C# этот метод выглядел бы примерно так:
public static void MyMethod(int inputInt, ref int refInt, ArrayList ar, out int outputInt)
{
outputInt = 0;
}
Отобразив этот метод на CIL, можно увидеть, что ссылочные параметры C# в CIL снабжаются символом амперсанда (&), который присоединяется в виде суффикса к типу параметра (int32&); выходные параметры тоже снабжаются суффиксом &, но при этом дополнительно помечаются лексемой [out]; когда параметр представляет собой параметр ссылочного типа (подобно [mscorlib] System.Collections.ArrayList), перед названием его типа данных в качестве префикса добавляется лексема class (которую ни в коем случае не следует путать с директивой .class):
// Метод, принимающий параметры
.method public hidebysig static void SomeMethod (int32 inputInt,
int32& refInt,
class [mscorlib]System.Collections.ArrayList ar,
[out] int32& outputInt) cil managed
{
// Какой-то код
}