Почему невозможно получить имена локальных переменных с помощью Reflection?

Если у меня есть такой код:

public class Program
{ public static void Main() { string bar = ""; int foo = 24; }
}

Я могу получить локальные переменные, объявленные в Main, используя:

var flag = BindingFlags.Static | BindingFlags.Public;
var fields = typeof(Program).GetMethod("Main", flags).GetMethodBody().LocalVariables;

Это возвращает a IList, а LocalVariableInfo имеет только три свойства: IsPinned, LocalIndex и LocalType. Существует также свойство Name.

Что мне интересно, так это то, что вы видите имена переменных в сгенерированном IL code:

.method public hidebysig static void Main() cil managed
{ .entrypoint // Code size 11 (0xb) .maxstack 1 .locals init ([0] string bar, [1] int32 foo) IL_0000: nop IL_0001: ldstr "" IL_0006: stloc.0 IL_0007: ldc.i4.s 24 IL_0009: stloc.1 IL_000a: ret
} // end of method Program::Main

но их невозможно использовать с помощью Reflection. Это потому, что локальные переменные не имеют имени, и к ним доступны только их индексы (если так, как ILDASM.exe показывает имена?), или потому что такая функция не реализована? Или, если это возможно, используя другой способ, тогда будет вопрос: как?

Примечание. Я видел такие вопросы, как this, и большинство из них использует Expressions для получения имени переменной. Это не работает, если я хотел бы получить все локальные жители, включая временные переменные, сгенерированные компилятором.

4 ответа

Вы должны различать понятную для человека текстовую форму CLI и машиночитаемую скомпилированную форму CLI.

В текстовом CLI локальные переменные действительно могут иметь имена (см. §II.15.4.1.3 ECMA-335, как объяснено в ответе Дэмиена).

Но в двоичной форме локальные переменные не имеют имен. Для этого рассмотрим §II.23.2.6, где указан бинарный формат для локальной сигнатуры локальной переменной метода (список всех ее локальных переменных). И он не содержит упоминания имен переменных:

Итак, если какой-либо инструмент хочет узнать исходное имя локальной переменной, он должен изучить информацию об отладке, содержащуюся в файле PDB. Если этого нет, нет способа узнать имя.


Я думаю, вы смотрите на сборку Debug. Часть id объявления .locals является необязательной, поэтому нет гарантии, что имена сохранены.

См. MS Partition II, в котором более подробно описаны метаданные IL, раздел 15.4.1.3:

MethodBodyItem ::= … .locals [ init ] ‘(’ LocalsSignature ‘)’
LocalsSignature ::= Local [ ‘,’ Local ]*
Local ::= Type [ Id ]


Из MSDN:

Локальные имена переменных не сохраняются в метаданных. В промежуточном языке Microsoft (MSIL) доступ к локальным переменным осуществляется по их позиции в локальной сигнатуре переменной.


Я думаю, это потому, что имя переменной, которое вы видите в ILDasm, происходит из файла pdb, а не из самой сборки. Если вы хотите их получить, вам также нужно будет прочитать pdb.

licensed under cc by-sa 3.0 with attribution.