C# 以“结束”结尾的变量;1“;有",;1“;在ILSpy中删除。为什么?
为了探索C#编译器如何优化代码,我创建了一个简单的测试应用程序。每次测试更改后,我都会编译应用程序,然后在ILSpy中打开二进制文件C# 以“结束”结尾的变量;1“;有",;1“;在ILSpy中删除。为什么?,c#,ilspy,C#,Ilspy,为了探索C#编译器如何优化代码,我创建了一个简单的测试应用程序。每次测试更改后,我都会编译应用程序,然后在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)
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");
}
}
}
}
我只是注意到一些对我来说很奇怪的事情。显然这是故意的,但是,我想不出一个好的理由来解释为什么编译器会这样做
考虑以下代码:
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\u 1
优化为test\u
!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");
}
}
}
}
更新
显然,在检查了IL之后,这是ILSpy的问题,而不是C#编译器的问题。尤金·波德斯卡尔对我最初的评论和观察给出了一个很好的答案。然而,我很想知道这是ILSpy内部的一个bug,还是有意的功能。这可能是反编译器的一些问题。因为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
形式的变量
编辑:
方法中的文件中的变量名损坏:
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;
}
}
其中无数字名称
为测试
编辑
首先,感谢你在这篇文章中指出错误
因此,问题的根源是:
ILSpy与ildasm一样智能,因为它也使用.pdb数据(或者它如何获得test\u 1、test\u 2
名称)。但是,它的内部工作是针对没有任何调试相关信息的程序集进行优化的,因此它与处理V_0、V_1、V_2
变量相关的优化与.pdb文件中丰富的元数据不一致
据我所知,罪魁祸首是从单个变量中删除\u 0
的优化
修复它可能需要将.pdb数据使用的事实传播到变量名生成代码中。嗯,这是一个bug。不算什么bug,几乎不可能有人为此提交bug报告。请注意,尤金的回答非常误导。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
等名字当然不是很好,反汇编程序通常会想出更好的方法。有点像“num”
因此,有点清楚ILSpy中的bug所在的位置,它也读取PDB文件,但却在检索符号时出错。您可以向供应商提交错误,但他们不太可能将其视为高优先级错误。是的,他们是。这就是为什么您可以编译/反编译程序集,并查看指定反编译代码的完整代码。这是由ILSpy之类的IL解释器完成的,它可以将编译后的.Net程序集输出到您选择的任何.Net语言。这决不能发生,至少不是通过自身或编译器。让我检查ildasm中的IL…@AfzaalAhmadZeeshan我能够使用发布的代码复制此内容。这是ILSpy的问题,或者更一般地说是反编译器的问题
dotPeek
显示变量名称的num1
,num2
num3
。谢谢。这就是答案——我应该更进一步。当我检查IL时,我自己发现了这个我猜这是ILSpy中的一个bug。看到这一点,我们可能看到反编译器正在删除“1”,因为某些带下划线的名称似乎是“maical”——例如由“property{get;set;}”或lambdasI生成的匿名字段会删除这个问题,但由于许多人使用ILSpy,我把它留着以后参考。您将在大约5分钟内得到答案。@EugenePodskal我已更新了我的问题,以反映ILSpy内部的好奇心。还有,事实上,有人否决了我?有时候我不明白,谢谢你,汉斯。你的答案总是很有用的!