Определение членов типов CIL

69

Как уже известно, типы в .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
		{
		    // Какой-то код
		}
Пройди тесты
Лучший чат для C# программистов