Формат файла .NET – внутреннее устройство сигнатур (часть 1) - MethodDefSig

ОГЛАВЛЕНИЕ

4.3 MethodDefSig

Данная сигнатура хранит информацию, связанную с методами, определенными в текущей сборке, такую как тип соглашения о вызовах, количество обобщенных параметров, количество нормальных параметров метода, тип возвращаемой переменной и тип каждого параметра, переданного методу. Она индексируется столбцом MethodDef.Signature.

Рисунок 6. Схема синтаксиса сигнатуры MethodDefSig

Дополнительно используются несколько флагов (перечислены в таблице ниже), они объединяются с помощью OR и размещаются во втором байте сигнатуры (первый байт – размер сигнатуры).

Имя

Значение

Что означает

HASTHIS

0x20

Первый передаваемый методу аргумент - указатель this, этот флаг установлен, когда метод – экземпляр или виртуальный. Вы также можете прочитать объяснение флага HASTHIS в предыдущем подразделе

EXPLICITTHIS

0x40

Спецификация гласит: "Обычно список параметров (всегда сопровождающий соглашение о вызовах) не предоставляет информацию о типе указателя this, так как его можно получить из другой информации. Когда явный экземпляр комбинации задан, все же, первый тип в последующем списке параметров задает тип указателя this, а следующие элементы задают типы самих параметров". Заметьте, что если EXPLICITTHIS установлен, HASTHIS тоже должен быть установлен.

DEFAULT

0x00

Позволяет общеязыковой среде исполнения определить соглашение о вызовах, этот флаг установлен при вызове статических методов.

VARARG

0x05

Задает соглашение о вызовах для методов с изменяющимися аргументами.

GENERIC

0x10

Метод имеет один или больше обобщенных параметров.

Пример 1

Как водится, начнем с простого примера: на этот раз мы создали метод экземпляра, имеющий два обобщенных параметра и два нормальных параметра, для упрощения метод не имеет никакого тела.

// Полный исходник: MethodDefSig\1.cs 
// Бинарный файл: MethodDefSig\1.dll
// (...)

public void TestMethod<GenArg1, GenArg2>(int Param1, object Param2) { }

Сигнатура MethodDefSig для примера метода хранится в смещении 0x000A и выглядит так.

Смещение

Значение

Что означает

0x0A

0x06

Размер сигнатуры

0x0B

0x30

Так как это экземпляр и обобщенный метод, флаги HASTHIS и GENERIC установлены, 0x20 OR 0x10 = 0x30.

0x0C

0x02

Количество обобщенных параметров.

0x0D

0x02

Количество нормальных параметров.

0x0E

0x01

Тип возвращаемого значения (void), смотрите константы.

0x0F

0x08

Тип первого параметра (int32), смотрите константы.

0x10

0x1C

Тип второго параметра (object), смотрите константы.

Пример 2

В данном примере снова показано использование флага HASTHIS, определение рассматриваемого метода выглядит так.

// Полный исходник: MethodDefSig\2.cs 
// Двоичный файл: MethodDefSig\2.dll
// (...)

public class TestClas
{
public static void TestMethod(int Param1, object Param2) { }
}

Сигнатура снова хранится в 0x000A в куче #Blob и выглядит так.

Смещение

Значение

Что означает

0x0A

0x05

Размер сигнатуры

0x0B

0x00

Есть один установленный флаг, а именно DEFAULT –  это означает, что метод статический, и позволяет CLR определить используемое соглашение о вызовах. Метод не обобщенный, так как флаг GENERIC не установлен, следовательно, следующий байт задает количество нормальных (не обобщенных) параметров, передаваемых методу.

0x0C

0x02

Количество нормальных параметров.

0x0D

0x01

Тип возвращаемого значения (void), смотрите константы.

0x0E

0x08

Тип первого параметра (int32), смотрите константы.

0x0F

0x1C

Тип второго параметра (object), смотрите константы.


Пример 3

Теперь посмотрим, как работает флаг EXPLICITTHIS: его можно включить путем использования ключевого слова explicit в определении метода, разумеется, на языке CIL.

// Полный исходник: MethodDefSig\3.il
// двоичный файл: MethodDefSig\3.dll
// (...)

.method instance explicit void TestMethod () cil managed
{
.maxstack 2
ret
}

MethodDefSig для вышеуказанного метода выглядит так.

Смещение

Значение

Что означает

0x01

0x03

Размер сигнатуры

0x02

0x60

Флаги HASTHIS и EXPLICITTHIS установлены, так как 0x20 OR 0x40 = 0x60.

0x03

0x00

Количество параметров, принимаемых методом.

0x04

0x01

Тип возвращаемого значения (void), смотрите константы.

Пример 4

В этом примере был создан метод, принимающий изменяемые аргументы, т.е. кроме нормальных параметров, заданных в объявлении, он принимает переменное количество параметров изменяемого типа. Добавление vararg в ключевое слово языка CIL в определение метода заставляет метод принимать изменяемые аргументы, что видно по листингу кода ниже.
Важно: Использование ключевого слова params в C# не устанавливает флаг VARARG в связанной с методом сигнатуре. Метод, использующий ключевое слово params в C#, всего лишь оформляется компилятором C# атрибутом ParamArray, и дополнительные параметры рассматриваются как нормальный массив. Можно сделать метод истинно VARARG в C#, следуя данной инструкции, но это не совместимо с CLS.

// Полный исходник: MethodDefSig\4.il
// Двоичный файл: MethodDefSig\4.dll
// (...)

.method instance vararg void TestMethod () cil managed
{
.maxstack 2
ret
}

Сигнатура метода рассмотрена в следующей таблице.

Смещение

Значение

Что означает

0x01

0x03

Размер сигнатуры

0x02

0x25

Метод является экземпляром и принимает изменяемые аргументы, следовательно, флаги HASTHIS и VARARG установлены, и 0x20 OR 0x05 = 0x25

0x03

0x00

Количество параметров, принимаемых методом.

0x04

0x01

Тип возвращаемого значения (void), смотрите константы.