【问题标题】:If System.Double initializes the constant field "NaN" to double.NaN, does it mean that "Double" and "double" don't refer to the same type?如果 System.Double 将常量字段“NaN”初始化为 double.NaN,是否意味着“Double”和“double”不是指同一个类型?
【发布时间】:2014-02-14 12:41:41
【问题描述】:

在 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;

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

public const double NaN = double.NaN;
//public const double NegativeInfinity = double.NegativeInfinity;
//public const double PositiveInfinity = double.PositiveInfinity;

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

现在,大多数 .NET 书籍教导的第一件事是标识符“Double”和关键字“double”都引用相同的类型(int 和 Int32、string 和 String 等相同),并且对任何怀疑这一点的人来说,快速浏览一下 IL 就足以证明这一点。

考虑到这一点,为什么 System.Double 类型可以避免将 double 类型的常量字段初始化为自身?是不是这个意思,

...适用于仅由 .NET 程序员编写的代码的规则不适用于内部 .NET 代码? ...反汇编程序弄错了吗? ...“Double”和“double”真的不是同一类型吗?

【问题讨论】:

    标签: c# .net il disassembly


    【解决方案1】:

    double 是 C# 关键字 - System.Double 类型的别名。您的假设是 mscorlib.dll 是用 C# 编写的。另外,反编译器是错误的。 NaN 的常量作为明确的双精度值存在 - 反编译器只是希望您希望将该常量视为 double.NaN 而不是 double.NaN(您可能不这样做)甚至不知道)。

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

    const int Zero = 0;
    
    Console.Write(Zero);
    

    代码(不是声明)的编译方式与

    Console.Write(0);
    

    理解这一点非常重要,因为常量值在编译时解析,而不是在运行时解析。如果您从引用的库中获取常量,并且该引用的库中常量的值发生变化,您的代码中将保留旧值,直到您针对新版本重新编译。

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

    static int Zero { get { return 0; } }
    

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

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

    想象一个方法:

    public void DoSomething(bool isSafe = true);
    

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

    public void DoSomething(bool isSafe = false);
    

    每个调用你的方法为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"
    

    既然我们已经在谈论枚举,是的,它们也有同样的问题。它们由它们的底层“数字”表示。如果你改变它,旧的数字会突然指向一个不同的“枚举值”,反之亦然——如果你创建或使用一些 API,这又是一个大问题:)

    【讨论】:

      【解决方案2】:

      是的,它们是同一类型。小写的double 是为了方便编译。 NaN 的定义是您的反编译器对您隐藏的特殊值。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多