如果System.Double将常量字段"NaN"初始化为double.NaN,是否意味着"Double"和"double"不是指同一类型?

J S*_*ith 2 .net c# il disassembly

在Visual Studio C#编辑器中,当您右键单击关键字"double"或标识符"Double"并选择"Go to Definition"时,VS将告诉您"NaN"已声明并初始化如下:

 public const double NaN = 0.0 / 0.0;
 //public const double NegativeInfinity = -1.0 / 0.0;
 //public const double PositiveInfinity = 1.0 / 0.0;
Run Code Online (Sandbox Code Playgroud)

但是,从mscorlib.dll中反汇编的System.Double的源代码讲述了一个不同的故事:

public const double NaN = double.NaN;
//public const double NegativeInfinity = double.NegativeInfinity;
//public const double PositiveInfinity = double.PositiveInfinity;
Run Code Online (Sandbox Code Playgroud)

当然,.NET不允许使用对自身的引用(循环定义)或带有对自身的引用的实例字段来初始化const字段(不允许使用对实例字段的引用来初始化实例字段),而可以通过引用自身来初始化静态字段,这显然不是这里的情况.

现在,大多数.NET书籍教授的第一件事就是标识符"Double"和关键字"double"都指向相同的类型(int和Int32,string和String等相同),并且快速查看在IL应该足以证明任何怀疑这一点的人.

考虑到这一点,为什么System.Double类型可以通过将double类型的常量字段初始化为自身而逃脱?这是不是意味着,

...适用于仅由.NET程序员编写的代码的规则不适用于内部.NET代码?......反汇编者弄错了?......"双"和"双"并不是真的相同?

Lua*_*aan 8

double是一个C#关键字 - 该类型的别名System.Double.你的假设是mscorlib.dll用C#编写的.此外,反编译器是错误的.该常数NaN是作为有一个明确的双重价值-反编译器只是希望,你希望看到常数double.NaN,而不是价值double.NaN(这你可能甚至不知道).

在编译的IL(在dll中),常量不会被名称引用.这是一件非常重要的事情.如果您这样做:

const int Zero = 0;

Console.Write(Zero);
Run Code Online (Sandbox Code Playgroud)

代码(不是声明)将编译为

Console.Write(0);
Run Code Online (Sandbox Code Playgroud)

这是一个非常重要的要点,因为常量值在编译时解决,而不是在运行时解析.如果从引用的库中获取常量,并且该引用库中常量的值发生更改,则在代码中将保留旧值,直到重新编译新的verison.

如果你需要一个可以在库版本之间更改的"常量",你最好使用静态属性,例如:

static int Zero { get { return 0; } }
Run Code Online (Sandbox Code Playgroud)

那么引用的实际上是属性,而不是它的值.

C#中的默认参数存在相同的行为.它们也在编译时解析,因此它们没有使构造函数/方法重载过时:)

想象一个方法:

public void DoSomething(bool isSafe = true);
Run Code Online (Sandbox Code Playgroud)

一切都很好.但是你决定默认行为应该是不安全的,而不是安全的.或者更糟糕的是,你改变了布尔值的含义(当你使用像flag:) 这样的参数名时,这是一个很重要的问题.所以你改变了定义

public void DoSomething(bool isSafe = false);
Run Code Online (Sandbox Code Playgroud)

调用你的方法的每个人DoSomething()都会用参数的旧值来调用它true.直到他们重新编译.如果您不理解何时解析默认参数,这可能非常令人沮丧且难以调试:)

回到别名关键字 - 有些情况,两者不一样.但是,这是因为编译器解析某些表达式的方式.主要的例子是enum关键字:

enum MyEnum : byte {} // Valid, an enum with an underlying type of byte.

enum MyEnum2 : System.Byte {} // Invalid, unexpected token. 
// "Type byte, sbyte, short, ushort, int, uint, long, or ulong expected"
Run Code Online (Sandbox Code Playgroud)

既然我们已经在谈论枚举,是的,那些也有同样的问题.它们由潜在的"数字"代表.如果你改变它,旧的数字突然指向一个不同的"枚举值",反之亦然 - 再次,如果你创建或使用一些API :)一个大问题:)