简体   繁体   English

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

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

In an effort to explore how the C# compiler optimizes code, I've created a simple test application. 为了探索C#编译器如何优化代码,我创建了一个简单的测试应用程序。 With each test change, I've compiled the application and then opened the binary in ILSpy. 每次测试更改,我都编译了应用程序,然后在ILSpy中打开了二进制文件。

I just noticed something that, to me, is weird. 我只是注意到一些对我来说很奇怪的东西。 Obviously this is intentional, however, I can't think of a good reason why the compiler would do this. 显然这是有意的,但是,我想不出编译器会这么做的一个很好的理由。

Consider the following code: 请考虑以下代码:

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");
}

Pointless code, but I had written this to see how ILSpy would interpret the if statements. 无意义的代码,但我写了这个,看看ILSpy如何解释if语句。

However, when I compiled/decompiled this code, I did notice something that had me scratching my head. 但是,当我编译/反编译这段代码时,我注意到了让我摸不着头脑的东西。 My first variable test_1 was optimized to test_ ! 我的第一个变量test_1被优化为test_ Is there a good reason why the C# compiler would do this? 有没有一个很好的理由为什么C#编译器会这样做?

For full inspection this is the output of Main() that I'm seeing in ILSpy. 对于全面检查,这是我在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 UPDATE

Apparently after inspecting the IL, this is an issue with ILSpy, not the C# compiler. 显然在检查IL之后,这是ILSpy的问题,而不是C#编译器。 Eugene Podskal has given a good answer to my initial comments and observations. Eugene Podskal对我的初步评论和观察给出了很好的答案。 However, I am interested in knowing if this is rather a bug within ILSpy or if this is intentional functionality. 但是,我有兴趣知道这是ILSpy中的错误还是故意功能。

It is probably some problem with decompiler. 这可能是反编译器的一些问题。 Because IL is correct on .NET 4.5 VS2013: 因为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

edit: it uses data from .pdb file(see this answer ) to get correct name variables. 编辑:它使用.pdb文件中的数据(请参阅此答案 )以获取正确的名称变量。 Without pdb it will have variables in form V_0, V_1, V_2 . 没有pdb,它将具有V_0, V_1, V_2形式的变量。

EDIT: 编辑:

Variable name mangles in the file NameVariables.cs in method: 方法中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 class uses this.typeNames dictionary to store names of variables without ending number (such variables mean something special to ILSpy, or perhaps even to IL, but I actually doubt it) associated with counter of their appearances in the method to decompile. NameVariables类使用this.typeNames字典来存储没有结束编号的变量名称(这些变量对ILSpy来说是特殊的,或者甚至可能是IL,但实际上我怀疑它)与反编译方法中出现的计数器相关联。

It means that all variables ( test_1, test_2, test_3 ) will end in one slot ("test_") and for the first one count var will be one, resulting in execution: 这意味着所有变量( test_1, test_2, test_3 )将在一个槽(“test_”)中结束,而第一个变量count将为1,从而导致执行:

else {
    return nameWithoutDigits;
}

where nameWithoutDigits is test_ 其中nameWithoutDigitstest_

EDIT 编辑

First, thanks @HansPassant and his answer for pointing the fault in this post. 首先,感谢@HansPassant和他在这篇文章中指出错误的答案

So, the source of the problem: 那么,问题的根源:

ILSpy is as smart as ildasm, because it also uses .pdb data (or how else does it get test_1, test_2 names at all). ILSpy和ildasm一样聪明,因为它也使用.pdb数据(或者它如何获得test_1, test_2名称)。 But its inner workings are optimized for use with assemblies without any debug related info, hence its optimizations related to dealing with V_0, V_1, V_2 variables works inconsistently with the wealth of metadata from .pdb file. 但其内部工作方式已经过优化,无需任何调试相关信息即可与程序集一起使用,因此与处理V_0, V_1, V_2变量相关的优化与.pdb文件中丰富的元数据不一致。

As I understand, the culprit is an optimization to remove _0 from lone variables. 据我所知,罪魁祸首是从单个变量中删除_0的优化。

Fixing it will probably require propagating of the fact of .pdb data usage into the variable name generations code. 修复它可能需要将.pdb数据使用的事实传播到变量名称生成代码中。

Well, it is a bug. 嗯,这是一个错误。 Not much of a bug, fairly unlikely that anybody ever filed a bug report for it. 没有太大的错误,任何人都不可能提交错误报告。 Do note that Eugene's answer is very misleading. 请注意尤金的答案是非常误导的。 ildasm.exe is smart enough to know how to locate the PDB file for an assembly and retrieve debugging info for the assembly. ildasm.exe足够聪明,知道如何找到程序集的PDB文件并检索程序集的调试信息。 Which includes the names of local variables. 其中包括局部变量的名称。

This is not normally a luxury available to a disassembler. 这通常不是反汇编程序可用的奢侈品。 Those names are not actually present in the assembly itself and they invariably have to make-do without the PDB. 这些名称实际上并不存在于程序集本身中,并且它们总是必须在没有PDB的情况下完成。 Something you can see in ildasm.exe as well, just delete the .pdb files in the obj\\Release and bin\\Release directories and it now looks like this: 你可以在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...

Names like V_0 , V_1 etcetera are of course not great, a disassembler usually comes up with something better. V_0V_1等名称当然不是很好,反汇编程序通常会提供更好的东西。 Something like "num". 像“num”这样的东西。

So, kinda clear where the bug in ILSpy is located, it too reads the PDB file but fumbles the symbol it retrieves. 因此,有点清楚ILSpy中的错误所在的位置,它也会读取PDB文件,但会找到它检索到的符号。 You could file the bug with the vendor, pretty unlikely they'll treat it as a high-priority bug however. 您可以向供应商提交错误,但不太可能将它视为高优先级错误。

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

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