为了探索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中看到的ILSpy的输出。
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");
}
}
}
}更新
显然,在检查IL之后,这是ILSpy的问题,而不是C#编译器的问题。尤金·波茨卡尔( Eugene Podskal )对我最初的评论和观察给出了一个很好的答案。但是,我想知道这是否是ILSpy中的一个bug,或者这是否是有意的功能。
发布于 2014-09-06 15:11:45
嗯,这是个虫子。这并不是什么错误,也不太可能有人为此提交了错误报告。请注意,尤金的回答非常误导人。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_0,V_1等名字当然不是很好,反汇编程序通常会想出更好的方法。有点像“数字”。
所以,很清楚ILSpy中的bug在哪里,它也读取PDB文件,但是错误地显示了它检索到的符号。您可以将该错误提交给供应商,但是他们不太可能将其视为一个高优先级的bug。
发布于 2014-09-05 18:34:44
这可能是反编译的一些问题。因为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 var将为一个,因此执行:
else {
return nameWithoutDigits;
}其中nameWithoutDigits是test_
编辑
首先,感谢@HansPassant及其回答指出了这篇文章中的错误。
因此,问题的根源是:
ILSpy和ildasm一样聪明,因为它还使用.pdb数据(或者它是如何获得test_1, test_2名称的)。但是,它的内部工作方式是经过优化的,可以与程序集一起使用,而不需要任何调试相关信息,因此,与处理V_0, V_1, V_2变量有关的优化与来自.pdb文件的大量元数据不一致。
据我所知,罪魁祸首是将_0从单个变量中删除的优化。
修复它可能需要将.pdb数据使用的事实传播到变量名称代代代码中。
https://stackoverflow.com/questions/25691865
复制相似问题