Шрифт:
Интервал:
Закладка:
int32& refInt,
class [System.Runtime.Extensions]System.Collections.ArrayList ar,
[out] int32& outputInt) cil managed
{
...
}
Исследование кодов операций CIL
Последний аспект кода CIL, который будет здесь рассматриваться, связан с ролью разнообразных кодов операций. Вспомните, что код операции — это просто лексема CIL, используемая при построении логики реализации для заданного члена.
Все коды операций CIL (которых довольно много) могут быть разделены на три обширные категории:
• коды операций, которые управляют потоком выполнения программы ;
• коды операций, которые вычисляют выражения;
• коды операций, которые получают доступ к значениям в памяти (через параметры, локальные переменные и т.д.).
В табл. 19.4 описаны наиболее полезные коды операций, имеющие прямое отношение к логике реализации членов; они сгруппированы по функциональности.
Коды операций из следующей обширной категории (подмножество которых описано в табл. 19.5) применяются для загрузки (заталкивания) аргументов в виртуальный стек выполнения. Обратите внимание, что все эти ориентированные на загрузку коды операций имеют префикс Id (load — загрузить).
В дополнение к набору кодов операций, связанных с загрузкой, CIL предоставляет многочисленные коды операций, которые явно извлекают из стека самое верхнее значение. Как было показано в нескольких начальных примерах, извлечение значения из стека обычно предусматривает его сохранение во временном локальном хранилище с целью дальнейшего использования (наподобие параметра для предстоящего вызова метода). Многие коды операций, извлекающие текущее значение из виртуального стека выполнения, снабжены префиксом st (store — сохранить). В табл. 19.6 описаны некоторые распространенные коды операций.
Имейте в виду, что различные коды операций CIL будут неявно извлекать значения из стека во время выполнения своих задач. Например, при вычитании одного числа из другого с применением кода операции sub должно быть очевидным то, что перед самим вычислением операция sub должна извлечь из стека два следующих доступных значения. Результат вычисления снова помещается в стек.
Директива .maxstack
При написании кода реализации методов на низкоуровневом языке CIL необходимо помнить о специальной директиве под названием .maxstack. С ее помощью устанавливается максимальное количество переменных, которые могут находиться внутри стека в любой заданный момент времени на протяжении периода выполнения метода. Хорошая новость в том, что директива .maxstack имеет стандартное значение (8), которое должно подойти для подавляющего большинства создаваемых методов. Тем не менее, если вы хотите указывать все явно, то можете вручную подсчитать количество локальных переменных в стеке и определить это значение явно:
(window.adrunTag = window.adrunTag || []).push({v: 1, el: 'adrun-4-390', c: 4, b: 390}).method public hidebysig instance void
Speak() cil managed
{
<b> // Внутри области действия этого метода в стеке находится</b>
<b> // в точности одно значение (строковый литерал).</b>
<b> .maxstack 1</b>
ldstr "Hello there..."
call void [mscorlib]System.Console::WriteLine(string)
ret
}
Объявление локальных переменных в CIL
Теперь давайте посмотрим, как объявлять локальные переменные. Предположим, что необходимо построить в CIL метод по имени MyLocalVariables(), который не принимает аргументы и возвращает void, и определить в нем три локальные переменные с типами System.String, System.Int32 и System.Object. В C# такой метод выглядел бы следующим образом (вспомните, что локальные переменные не получают стандартные значения и потому перед использованием должны быть инициализированы):
public static void MyLocalVariables()
{
string myStr = "CIL code is fun!";
int myInt = 33;
object myObj = new object();
}
А вот как реализовать метод MyLocalVariables() на языке CIL:
.method public hidebysig static void
MyLocalVariables() cil managed
{
.maxstack 8
<b> // Определить три локальные переменные.</b>
.locals init (string myStr, int32 myInt, object myObj)
<b> // Загрузить строку в виртуальный стек выполнения.</b>
ldstr "CIL code is fun!"
<b> // Извлечь текущее значение и сохранить его в локальной переменной [0].</b>
stloc.0
<b> // Загрузить константу типа i4 (сокращение для int32) со значением 33.</b>
ldc.i4.s 33
<b> // Извлечь текущее значение и сохранить его в локальной переменной [1].</b>
stloc.1
<b> // Создать новый объект и поместить его в стек.</b>
newobj instance void [mscorlib]System.Object::.ctor()
<b> // Извлечь текущее значение и сохранить его в локальной переменной [2].</b>
stloc.2
ret
- Понимание SQL - Мартин Грубер - Базы данных