我正在使用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所做的似乎是不可能的。
注意:即使在设置中未选中“使用调试符号中的变量名称(如果可用)”和“显示调试符号中的信息(如果可用)”选项,也会出现此行为。
正如你在这个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等)。
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来解决文档/源代码不一致。