Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Троелсен Э. Язык программирования С# 2010 и п...docx
Скачиваний:
108
Добавлен:
21.09.2019
Размер:
6.92 Mб
Скачать

Директива .Maxstack

При реализации метода непосредственно средствами CIL нужно помнить о специальной директиве, которая называется .maxstack. Как следует из ее названия, директива .maxstack задает максимальное число переменных, которые может вместить стек в любой момент времени при выполнении метода. К счастью, директива .maxstack имеет значение по умолчанию (8), которого оказывается достаточно для подавляющего большинства методов, создаваемых разработчиками. Но у вас также есть возможность определить это значение явно, чтобы при желании вручную указать числа локальных переменных в стеке.

.method public hidebysig instanсе void Speak() cil managed {

 // В контексте этого метода в стек помещается ровно

 // одно значение (строковый литерал).

 .maxstack 1

 ldstr "Всем привет…"

 call void [mscorlib]System.Consolr::WriteLine(string)

 ret

}

Объявление локальных переменных

Давайте выясним, как объявляются локальные переменные. Предположим, что мы должны построить в терминах CIL метод MyLocalVariables(), не имеющий никаких аргументов и возвращающий void. В этом методе мы должны определить три локальные переменные типов System.String, System.Int32 и System.Object. В C# соответствующий программный код мог бы выглядеть так, как показано ниже (напомним, что локальные переменные не получают значения по умолчанию, поэтому им перед использованием необходимо присвоить начальные значения).

public static void MyLocalVariables() {

 string myStr = "CIL me dude…";

 int myInt = 33;

 object myObj = new object();

}

Если создавать MyLocalVariables() непосредственно в CIL, можно было бы написать следующее,

.method public hidebysig static void MyLocalVariables() cil managed {

 .maxstack 6

 // Определение трех локальных переданных.

 .locals init ([0] string myStr, [1]int32 myInt, [2]object myObj)

 // Загрузка строки в виртуальный стек выполнения.

ldstr "CIL me dude…"

 // Извлечение текущего значения и сохранение его

 // в локальной переменной [0].

 stloc.0

 // Загрузка константы типа 'i4'

 // (сокращение для int32) со значением 33.

 ldc.i4 33

 // Извлечение текущего значения и сохранение его

 // в локальной переменной [1].

 stloc.1

 // Создание нового объекта и помещение его в стек.

newobj instance void [mscorlib]System.Object::.ctor()

 // Извлечение текущего значения и сохранение его

 // в локальной переменной [2].

 stloc.2

 ret

}

Как видите, в CIL при размещении локальных переменных сначала используется директива .locals с атрибутом init. При этом в скобках каждая переменная связывается со своим числовым индексом (здесь это [0], [1] и [2]). Каждый индекс идентифицируется типом данных и (необязательно) именем переменной. После определения локальных переменных соответствующее значение загружается в стек (с помощью подходящих кодов операций, связанных с загрузкой) и запоминается в локальной переменной (с помощью подходящих кодов операций для сохранения значений).

Связывание параметров с локальными переменными

Вы только что видели, как в CIL с помощью .local init объявляются локальные переменные, однако нужно еще выяснить, как передать отступающие параметры локальным методом. Рассмотрим следующий статический метод C#.

public static int Add(int aint b) {

 return a + b;

}

Этот внешне "невинный" метод в терминах CIL существенно более "многословен". Во-первых, поступающие аргументы (а и b) следует поместить в виртуальный стек выполнение с помощью кода операций ldarg (загрузка аргумента). Затем используется код операции add, чтобы извлечь два значения из стека, найти сумму и снова сохранить значение в стеке. Наконец, эта сумма извлекается из стена и возвращается вызывающей стороне с помощью кода операции ret. Если дизассемблировать указанный метод C# с помощью ildasm.exe, вы обнаружите, что компилятор csc.exe добавляет множество дополнительных лексем, хотя сущность CIL-кода оказывается исключительно простой.

.method public hidebysig static int32 Add(int32 a, int32 b) cil managed {

 .maxstack 2

 ldarg.0 // Загрузка 'a' в стек,

 ldarg.1 // Загрузка 'b' стек,

 add // Сложение этих значений.

 ret

}