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

ОГЛАВЛЕНИЕ

 4.4 MethodRefSig

Эта сигнатура очень похожа (если не идентична) на ранее упомянутую MethodDefSig, но MethodRefSig описывает соглашение о вызовах метода, параметры, и т.д., в точке, где метод вызывается (также называемой точка вызова). Сигнатура индексируется столбцом MemberRef.Signature, и если метод не принимает изменяемые аргументы, сигнатура идентична MethodDefSig и должна точно совпадать с сигнатурой, указанной в определении целевого метода, в ином случае – как показано ниже.

 

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

При вызове метода VARARG в связанной с ним MethodRefSig имеется одна дополнительная константа, а именно SENTINEL. Данное значение предназначено лишь для одного - оно обозначает конец обязательных параметров, передаваемых методу, и начало дополнительных (изменяемых) параметров, дополнительную информацию о значениях сигнальной метки можно найти здесь. Также заметьте, что целочисленный ParamCount показывает общее число параметров, переданных методу. В таблице ниже перечислены все сокращения, используемые в сигнатуре MethodRefSig, когда она отличается от MethodDefSig.

Имя

Значение

Что означает

HASTHIS

0x20

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

EXPLICITTHIS

0x40

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

VARARG

0x05

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

SENTINEL

0x41

Обозначает конец обязательных параметров.

Пример 1

Дабы убедить вас, что при вызове не VARARG метода нет разницы между MethodDefSig и связанной с ней сигнатурой MethodRefSig, был создан следующий код.

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

public void TestMethod(int Param1, string Param2) { }
Collapse Copy Code
// Полный исходник: MethodRefSig\1b.cs
// Двоичный файл: MethodRefSig\1b.dll
// (...)

new TestClass().TestMethod(0, "A simple parameter");

Теперь рассмотрим сигнатуру TestMethod's MethodDefSig, хранящуюся в файле MethodRefSig\1a.dll.

Смещение

Значение

Что означает

0x0A

0x05

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

0x0B

0x20

Метод - экземпляр, следовательно, флаг HASTHIS установлен, что означает, что первый аргумент, переданный методу, - указатель this.

0x0C

0x02

Метод принимает точно два параметра.

0x0D

0x01

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

0x0E

0x08

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

0x0F

0x0E

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

Связанная с ней MethodRefSig выглядит точно так же, но хранится в другом смещении.

Смещение

Значение

Что означает

0x13

0x05

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

0x14

0x20

Метод - экземпляр, следовательно, флаг HASTHIS установлен, что означает, что первый аргумент, переданный методу, - указатель this.

0x15

0x02

Метод принимает точно два параметра.

0x16

0x01

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

0x17

0x08

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

0x18

0x0E

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

Пример 2

В этом примере показано, как сигнатура MethodRefSig обращается с вызовом методов VARARG. Для этой цели был создан подлинно VARARG метод, принимающий один обязательный параметр, и остальные, изменяемые параметры. Помните, что использование ключевого слова params в C# не устанавливает флаг VARARG в связанной с методом сигнатуре, так как params всего лишь оформляет метод атрибутом ParamArray, и дополнительные параметры рассматриваются как массив объектов некоторого типа. Чтобы установить флаг VARARG в сигнатуре, вы должны добавить __arglist в определение метода в качестве последнего параметра, но это не совместимо с CLS ( за дополнительной информацией идите сюда).

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

[CLSCompliant(false)]
public void TestMethod(string RequiredParam, __arglist)
{
Console.WriteLine("Required parameter is: " + RequiredParam);

Console.WriteLine("Additional parameters are: ");
ArgIterator argumentIterator = new ArgIterator(__arglist);
for (int i = 0; i < argumentIterator.GetRemainingCount(); i++)
{
Console.WriteLine(__refvalue(argumentIterator.GetNextArg(), string));
}

}

Теперь пришло время вызвать наш метод из отдельной сборки: метод вызывается с одним обязательным аргументом типа string, и с двумя дополнительными аргументами типа int32, как показано ниже.

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

[CLSCompliant(false)]
public void TestRunMethod()
{
new TestClass().TestMethod(
"I am required parameter.",
__arglist(0, 1));
}

Для вышеуказанного вызова имеются две строки в таблице MemberRef. Неясно, почему это так, но сигнатура из первой обнаруженной строки имеет установленный флаг HASTHIS, однако он не содержит никакой информации об изменяемых аргументах, переданных методу. Спецификация ничего не говорит о таком странном поведении, но сигнатура, проиндексированная второй строкой, правильная, так что давайте посмотрим.

Смещение

Значение

Что означает

0x23

0x07

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

0x24

0x25

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

0x25

0x03

Общее число параметров, переданных методу, равняется 3: один обязательный и два дополнительных.

0x26

0x01

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

0x27

0x0E

Тип первого обязательного параметра (string), смотрите константы.

0x28

0x41

Константа SENTINEL, все параметры после данного значения дополнительные.

0x29

0x08

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

0x30

0x08

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