反编译器如何识别编译常量?

问题描述 投票:3回答:2

我正在使用ILSpy来反编译.Net的程序集并查看代码。当我在System.Windows.Vector.AngleBetween(Vector, Vector)浏览WindowsBase.dll的代码时,我偶然发现了一些奇怪的东西。

这是函数的完整代码:

public static double AngleBetween(Vector vector1, Vector vector2)
{
    double y = vector1._x * vector2._y - vector2._x * vector1._y;
    double x = vector1._x * vector2._x + vector1._y * vector2._y;
    return Math.Atan2(y, x) * (180.0 / Math.PI);
}

显然ILSpy可以识别Math.PI,这是一个常数。

这就是Microsoft Docs关于C#常量的说法:

实际上,当编译器在C#源代码中遇到常量标识符时,它会将文字值直接替换为它生成的中间语言(IL)代码。

基于此,ILSpy所做的似乎是不可能的。

注意:即使在设置中未选中“使用调试符号中的变量名称(如果可用)”和“显示调试符号中的信息(如果可用)”选项,也会出现此行为。

c# decompiling compile-time-constant ilspy
2个回答
6
投票

正如你在这个ILSpy issue和相应的pull request中看到的那样,这是为Math.PI等众所周知的值特别实现的(硬编码)。

来自GitHub问题:

我想通过以下方式计算pi系数:c = Math.PI / constant。如果我们得到“好”值(完全等于1.0,2.0,0.5,1 / 180等),我们只需用符号表达式替换它(Math.PI,Math.PI * 2,Math.PI / 2,Math .PI / 180等)。


0
投票

LINQPad中的快速测试显示了生成的IL代码的一些差异(从here复制的PI常量值)。

资源:

void Main()
{
    double a = Math.PI;
    double b = 3.14159265358979;
}

IL:

IL_0000:  nop         
IL_0001:  ldc.r8      18 2D 44 54 FB 21 09 40 
IL_000A:  stloc.0     // a
IL_000B:  ldc.r8      11 2D 44 54 FB 21 09 40 
IL_0014:  stloc.1     // b
IL_0015:  ret   

在常量和字面值之间生成的IL代码似乎存在细微差别,但我不确定它到底意味着什么。


来自MSDN documentation的上述值似乎与来自.NET Reference Source的信息相矛盾(见评论)。通过来自源的调整代码,IL是相同的。

资源:

void Main()
{
    var a = Math.PI;
    var b = 3.14159265358979323846;
}

IL:

IL_0000:  nop         
IL_0001:  ldc.r8      18 2D 44 54 FB 21 09 40 
IL_000A:  stloc.0     // a
IL_000B:  ldc.r8      18 2D 44 54 FB 21 09 40 
IL_0014:  stloc.1     // b
IL_0015:  ret   

顺便说一下,看起来有一个open issue来解决文档/源代码不一致。

© www.soinside.com 2019 - 2024. All rights reserved.