Почему невозможно получить имена локальных переменных с помощью 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>, а 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 для получения имени переменной. Это не работает, если я хотел бы получить все локальные жители, включая временные переменные, сгенерированные компилятором.

Ответ 1

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

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

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

LocalVarSig

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

Ответ 2

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

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

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

Ответ 3

Из MSDN:

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

Ответ 4

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