Свойства и методы также имеют специальные представления в CIL. Чтобы в нашем примере обеспечить в MyBaseClass поддержку открытого свойства TheString, можно использовать следующий CIL-код (заметьте, что здесь опять используется атрибут specialname).
.class public MyBaseClass {
…
.methodpublic hidebysig specialnameinstance string get_TheString() cil managed {
// Задача: добавить необходимый программный код…
}
.methodpublic hidebysig specialname instance void set_TheString(string 'value') cil managed {
// Задача: добавить необходимый программный ход.…
}
.propertyinstance string TheString() {
.getinstance string MyNamespace.MyBaseClass::get_TheString()
.setinstance void MyNamespace.MyBaseClass::set_TheString(string)
}
}
Напомним, что в терминах CIL свойства будут представлены парой методов, имеющих префиксы get_ и set_. Директива .property использует соответствующие директивы .get и .set, чтобы связать синтаксис свойства со "специально именованными" методами.
Замечание.Указанные выше определения свойств компилироваться не будут, поскольку пока что не реализована сама логика чтения и модификации данных.
Определение параметров членов
Теперь предположим, что нужно определить методы, имеющие аргументы. По сути, указание аргументов в CIL (приблизительно) соответствует аналогичной операции в C#. Например, аргумент определяется с помощью указания типа данных после имени соответствующего параметра. К тому же, как и в C#, в CIL обеспечиваются возможности ввода, вывода и передачи параметров по ссылке. Также в CIL позволяется определять аргумент массива параметров (в C# это делается с помощью ключевого слова params) и необязательные параметры (которые в C# не поддерживаются, но допускаются в VB .NET).
Чтобы показать пример определения параметров непосредственно в CIL, предположим, что нам нужно построить метод, который получает int32 (по значению), int32 (по ссылке), [mscorlib] System.Collections.ArrayList и имеет единственный выходной параметр (типа int32). В терминах C# этот метод должен выглядеть приблизительно так.
public static void MyMethod(int inputInt, refint refInt, ArrayList ar, outint outputInt) {
outputInt = 0; // Просто чтобы удовлетворить компилятор C#…
}
Если спроецировать этот метод в CIL-код, вы обнаружите, что ссылки на параметры C# будут обозначены знаком амперсанда (&), добавленного в виде суффикса к типу данных, соответствующему параметру (int32&). Для выходных параметров тоже используется суффикс &, но, кроме того, они обозначены маркером CIL [out], Также обратите внимание на то, что в том случае, когда параметр является ссылочным типом (как тип [mscorlib]System.Collections.ArrayList в нашем примере), ему предшествует лексема class (не путайте с директивой .class!).
.method public hidebysig static void MyMethod(int32 inputInt, int32&refInt, class[mscorlib]System.Collections.ArrayList ar, [out] int32&outputInt) cil managed {
…
}
Анализ кодов операций CIL
Заключительной темой нашего обсуждения в этой главе в отношении программного кода CIL будет роль кодов операций. Напомним, что код операции – это просто лексема CIL, используемая для построения логики реализации данного члена. Полный набор кодов операций CIL (который сам по себе довольно велик) можно разбить на следующие большие категории.
• Коды операций для управления программой
• Коды операций для оценки выражений
• Коды операций для осуществления доступа к значениям в памяти (через параметры, локальные переменный и т.п.)
Чтобы продемонстрировать некоторые возможности реализации членов средствами CIL, в табл. 15.5 предлагаются описания некоторых из наиболее часто используемых кодов операций, непосредственно связанных с логикой реализации членов. Кроме того, коды операций в данной таблице сгруппированы по функциональности.
Таблица 15.5.Коды операций CIL, связанные с реализацией членов
| Коды операций |
Описание |
| add, sub, mul, div, rem |
Позволяют выполнять сложение, вычитание, умножение и деление для пар значений (rem возвращает остаток от деления) |
| and, or, not, xor |
Позволяют выполнять соответствующие бинарные операции для пар значений |
| ceq, cgt, clt |
Позволяют сравнивать пару значений из стека различными способами, например: ceq: сравнение в отношении равенства cgt: сравнение в отношении "больше" clt: сравнение в отношении "меньше" |
| box, unbox |
Используются для конвертирования ссылочных типов и типов, характеризуемых значениями |
| ret |
Используется для выхода из метода и (если это необходимо) возвращения значения вызывающей стороне |
| beq, bgt, ble, blt, switch |
Используются (в дополнение к множеству других родственных кодов операций) для управления логикой ветвления в методах, например: beq: переход к заданной метке, если выполняется равенство bgt: переход к заданной метке, если больше ble: переход к заданной метке, если меньше или равно blt: переход к заданной метке, если меньше Все коды операций, связанные с ветвлением, требуют указания метки CIL-кода, по которой должен осуществляться переход в том случае, когда соответствующее сравнение возвращает true |
| call |
Используется для вызова члена указанного типа |
| newarr, newobj |
Позволяет разместить в памяти новый массив или новый объект (cоответственно) |
Следующая большая категория кодов операций CIL (подмножество которой показано в табл. 15.6) используется для загрузки аргументов в виртуальный стек выполнения. Обратите внимание на то, что эти относящиеся к загрузке коды операций имеют префикс ld (load – загрузка).
Читать дальше