繁体   English   中英

以“1”结尾的变量在ILSpy中删除了“1”。 为什么?

[英]Variables ending with “1” have the “1” removed within ILSpy. Why?

为了探索C#编译器如何优化代码,我创建了一个简单的测试应用程序。 每次测试更改,我都编译了应用程序,然后在ILSpy中打开了二进制文件。

我只是注意到一些对我来说很奇怪的东西。 显然这是有意的,但是,我想不出编译器会这么做的一个很好的理由。

请考虑以下代码:

static void Main(string[] args)
{
    int test_1 = 1;
    int test_2 = 0;
    int test_3 = 0;

    if (test_1 == 1) Console.Write(1);
    else if (test_2 == 1) Console.Write(1);
    else if (test_3 == 1) Console.Write(2);
    else Console.Write("x");
}

无意义的代码,但我写了这个,看看ILSpy如何解释if语句。

但是,当我编译/反编译这段代码时,我注意到了让我摸不着头脑的东西。 我的第一个变量test_1被优化为test_ 有没有一个很好的理由为什么C#编译器会这样做?

对于全面检查,这是我在ILSpy中看到的Main()的输出。

private static void Main(string[] args)
{
    int test_ = 1; //Where did the "1" go at the end of the variable name???
    int test_2 = 0;
    int test_3 = 0;
    if (test_ == 1)
    {
        Console.Write(1);
    }
    else
    {
        if (test_2 == 1)
        {
            Console.Write(1);
        }
        else
        {
            if (test_3 == 1)
            {
                Console.Write(2);
            }
            else
            {
                Console.Write("x");
            }
        }
    }
}

UPDATE

显然在检查IL之后,这是ILSpy的问题,而不是C#编译器。 Eugene Podskal对我的初步评论和观察给出了很好的答案。 但是,我有兴趣知道这是ILSpy中的错误还是故意功能。

这可能是反编译器的一些问题。 因为IL在.NET 4.5 VS2013上是正确的:

.entrypoint
  // Code size       79 (0x4f)
  .maxstack  2
  .locals init ([0] int32 test_1,
           [1] int32 test_2,
           [2] int32 test_3,
           [3] bool CS$4$0000)
  IL_0000:  nop
  IL_0001:  ldc.i4.1
  IL_0002:  stloc.0

编辑:它使用.pdb文件中的数据(请参阅此答案 )以获取正确的名称变量。 没有pdb,它将具有V_0, V_1, V_2形式的变量。

编辑:

方法中NameVariables.cs文件中的变量名称变形

public string GetAlternativeName(string oldVariableName)
{
    if (oldVariableName.Length == 1 && oldVariableName[0] >= 'i' && oldVariableName[0] <= maxLoopVariableName) {
        for (char c = 'i'; c <= maxLoopVariableName; c++) {
            if (!typeNames.ContainsKey(c.ToString())) {
                typeNames.Add(c.ToString(), 1);
                return c.ToString();
            }
        }
    }

    int number;
    string nameWithoutDigits = SplitName(oldVariableName, out number);

    if (!typeNames.ContainsKey(nameWithoutDigits)) {
        typeNames.Add(nameWithoutDigits, number - 1);
    }

    int count = ++typeNames[nameWithoutDigits];

    if (count != 1) {
        return nameWithoutDigits + count.ToString();
    } else {
        return nameWithoutDigits;
    }
}

NameVariables类使用this.typeNames字典来存储没有结束编号的变量名称(这些变量对ILSpy来说是特殊的,或者甚至可能是IL,但实际上我怀疑它)与反编译方法中出现的计数器相关联。

这意味着所有变量( test_1, test_2, test_3 )将在一个槽(“test_”)中结束,而第一个变量count将为1,从而导致执行:

else {
    return nameWithoutDigits;
}

其中nameWithoutDigitstest_

编辑

首先,感谢@HansPassant和他在这篇文章中指出错误的答案

那么,问题的根源:

ILSpy和ildasm一样聪明,因为它也使用.pdb数据(或者它如何获得test_1, test_2名称)。 但其内部工作方式已经过优化,无需任何调试相关信息即可与程序集一起使用,因此与处理V_0, V_1, V_2变量相关的优化与.pdb文件中丰富的元数据不一致。

据我所知,罪魁祸首是从单个变量中删除_0的优化。

修复它可能需要将.pdb数据使用的事实传播到变量名称生成代码中。

嗯,这是一个错误。 没有太大的错误,任何人都不可能提交错误报告。 请注意尤金的答案是非常误导的。 ildasm.exe足够聪明,知道如何找到程序集的PDB文件并检索程序集的调试信息。 其中包括局部变量的名称。

这通常不是反汇编程序可用的奢侈品。 这些名称实际上并不存在于程序集本身中,并且它们总是必须在没有PDB的情况下完成。 你可以在ildasm.exe中看到的东西,只需删除obj \\ Release和bin \\ Release目录中的.pdb文件,它现在看起来像这样:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       50 (0x32)
  .maxstack  2
  .locals init (int32 V_0,
           int32 V_1,
           int32 V_2)
  IL_0000:  ldc.i4.1
  // etc...

V_0V_1等名称当然不是很好,反汇编程序通常会提供更好的东西。 像“num”这样的东西。

因此,有点清楚ILSpy中的错误所在的位置,它也会读取PDB文件,但会找到它检索到的符号。 您可以向供应商提交错误,但不太可能将它视为高优先级错误。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM