【问题标题】:Why is == overridden in System.Double but not in System.Int32 and what are the ramifications of that?为什么 == 在 System.Double 中被覆盖而不在 System.Int32 中被覆盖,这会产生什么后果?
【发布时间】:2014-06-05 23:05:25
【问题描述】:

在C#中为什么Double会覆盖==Int32不会,效果如何?

我在看msdn库。

我看到这个关于 double 的链接并没有说太多 here (虽然我知道 double 是 Double 对象的简写)..它没有显示方法例如..

但是System.Double 上的这个链接确实提到了我正在寻找的东西here

它显示 Equality 运算符采用双精度,因此重载。

MSDN 中Double 的图像,然后在方法之后(在列出字段之前),它显示运算符,并显示相等运算符被覆盖

我可以点击“运算符”下的平等和it says

public static bool operator ==(
    double left,
    double right
)

而当我看到System.Int32 MSDN中Int32的图片如下

看那张图片,(System.Int32 的页面)看起来== 没有被覆盖。

为什么,这样做的后果是什么?

【问题讨论】:

  • 看起来文档不完整或幕后发生了一些事情,因为从文档页面中它说:“Int32 类型支持标准数学运算,例如加法、减法、除法、乘法、否定和一元否定。与其他整数类型一样,Int32 类型也支持按位与、或、异或、左移和右移运算符。您可以使用标准数值运算符来比较两个 Int32 值,也可以调用 CompareTo 或 Equals 方法。”
  • @TyCobb 使用 ILSpy 发帖的人表明该方法存在,所以这可能表明它不是生成的代码.. 他还建议它反编译。而且,我并不怀疑它调用了 Equals 方法,但是你能引用什么来说明这一点吗?
  • @TyCobb 正在查看 Matt 发布的内容。第一个和第三个 WriteLine() 给出不同的结果。所以 == 和 .Equals 的行为不一样。所以, == 不会调用 .Equals(..) ,同意吗?
  • @barlop 是的,毫无疑问。 TyCobb 也表明,对于 In32,== 不会调用 .Equals。没有理由认为他们中的任何一个都会发生这种情况。
  • 您不能实际调用运算符,C# 编译器具有内置的值类型知识,并且始终发出 Opcodes.Ceq。它们仅充当文档占位符,用于记录 IEEE-754 标准的怪异行为。

标签: c# operator-overloading


【解决方案1】:

Int32 在 .NET 方面似乎非常特别。源代码中缺少的功能很可能已融入系统核心。

如果不在结构中声明这些运算符,则无法将结构/值类型与==> 等进行比较。因为Int32 缺少这些,所以我得出了上面的结论。

做一个简单的测试并转储 IL,他们在做完全相同的比较,没有 CompareToEquals 被调用(我认为这确实发生了。我学到了一些东西!)。

public void TestInts()
{
    var x = 1;
    var y = 2;
    var equals = x == y;
}

.method public hidebysig 
    instance void TestInts () cil managed 
{
    // Method begins at RVA 0x2094
    // Code size 11 (0xb)
    .maxstack 2
    .locals init (
        [0] int32 x,
        [1] int32 y,
        [2] bool equals
    )

    IL_0000: nop
    IL_0001: ldc.i4.1
    IL_0002: stloc.0
    IL_0003: ldc.i4.2
    IL_0004: stloc.1
    IL_0005: ldloc.0
    IL_0006: ldloc.1
    IL_0007: ceq
    IL_0009: stloc.2
    IL_000a: ret
}

public void TestDoubles()
{
    var x = 1.7d;
    var y = 1.5d;
    var equals = x == y;
}
.method public hidebysig 
    instance void TestDoubles () cil managed 
{
    // Method begins at RVA 0x20ac
    // Code size 27 (0x1b)
    .maxstack 2
    .locals init (
        [0] float64 x,
        [1] float64 y,
        [2] bool equals
    )

    IL_0000: nop
    IL_0001: ldc.r8 1.7
    IL_000a: stloc.0
    IL_000b: ldc.r8 1.5
    IL_0014: stloc.1
    IL_0015: ldloc.0
    IL_0016: ldloc.1
    IL_0017: ceq
    IL_0019: stloc.2
    IL_001a: ret
}

上面的 IL 只是为这两种情况调用了标准 ceq opcode。根据 .NET 标准,Int32 应该在源代码中声明比较运算符,但它没有。

编辑:看起来好像所有整数值类型都是这样的。 Single、Double、Decimal 都有源代码中指定的运算符。 Int16、Int32、Int64、Byte,不要。

【讨论】:

  • @barlop 这有点相似。唯一的问题是答案是指引用相等,这在值类型方面没有实际意义。
  • 我认为您的 cmets 回答了我的问题的原因(您的强理论很好)。您之前只发表了我的问题(您已删除),如果我记得,暗示(正确地)没有任何后果。 (您将其删除是因为您在其中对 .equals 做出了错误的声明,您在答案中进行了更正)。但最终是来自人们的 cmets(包括您删除的一个可能是第一个,尽管现在很难检查!),这回答了我的问题。
  • 好点。我认为它会使用它,因为我认为 System.Int32 的一个实例是一个对象?整数比较运算符在 7.10.1 bool operator ==(int x, int y); 7.10.2 bool operator ==(double x, double y); 中提到了 double
  • 在您的编辑中很好地发现了整数。所以他们只在有 NaN 的类中。顺便一提。 System.Single 是浮动的。
  • @barlop 是的,我决定在我的 cmets 上打扫房间,因为他们感觉不对了。不知道为什么当我有 Single 时我会在那里拍浮子。将删除。
【解决方案2】:

一个可能的原因是因为 Double.NaN。

对于== operator:MSDN 说:如果使用相等运算符(==)测试两个 Double.NaN 值是否相等,则结果为 false;两个 Double.NaN 值不被视为相等。如果通过调用 Equals 方法测试它们是否相等,则结果为真。当您想确定 Double 的值是否不是数字 (NaN) 时,另一种方法是调用 IsNaN 方法。

所以 == 运算符和 Double 的 Equals 方法对于 Double.NaN 有不同的行为,我认为这就是为什么 == 被 double 覆盖的原因。至于 int,则没有这种特殊情况。

演示差异的代码:

using System;

public class Example
{
   public static void Main()
   {
      Console.WriteLine("NaN == NaN: {0}", Double.NaN == Double.NaN); 
      Console.WriteLine("NaN != NaN: {0}", Double.NaN != Double.NaN); 
      Console.WriteLine("NaN.Equals(NaN): {0}", Double.NaN.Equals(Double.NaN)); 
      Console.WriteLine("! NaN.Equals(NaN): {0}", ! Double.NaN.Equals(Double.NaN)); 
      Console.WriteLine("IsNaN: {0}", Double.IsNaN(Double.NaN));

      Console.WriteLine("\nNaN > NaN: {0}", Double.NaN > Double.NaN); 
      Console.WriteLine("NaN >= NaN: {0}", Double.NaN >= Double.NaN); 
      Console.WriteLine("NaN < NaN: {0}", Double.NaN < Double.NaN);
      Console.WriteLine("NaN < 100.0: {0}", Double.NaN < 100.0); 
      Console.WriteLine("NaN <= 100.0: {0}", Double.NaN <= 100.0); 
      Console.WriteLine("NaN >= 100.0: {0}", Double.NaN > 100.0);
      Console.WriteLine("NaN.CompareTo(NaN): {0}", Double.NaN.CompareTo(Double.NaN)); 
      Console.WriteLine("NaN.CompareTo(100.0): {0}", Double.NaN.CompareTo(100.0)); 
      Console.WriteLine("(100.0).CompareTo(Double.NaN): {0}", (100.0).CompareTo(Double.NaN)); 
   }
}
// The example displays the following output: 
//       NaN == NaN: False 
//       NaN != NaN: True 
//       NaN.Equals(NaN): True 
//       ! NaN.Equals(NaN): False 
//       IsNaN: True 
//        
//       NaN > NaN: False 
//       NaN >= NaN: False 
//       NaN < NaN: False 
//       NaN < 100.0: False 
//       NaN <= 100.0: False 
//       NaN >= 100.0: False 
//       NaN.CompareTo(NaN): 0 
//       NaN.CompareTo(100.0): -1 
//       (100.0).CompareTo(Double.NaN): 1

代码同样来自MSDN

【讨论】:

  • 你能提供任何可编译的代码示例来证明这种差异吗?
  • @TyCobb referencesource.microsoft.com/#mscorlib/system/double.cs#155 似乎没有为覆盖 == 提供任何有用的理由
  • 虽然该解释解释了公共 bool Equals(Double obj) 被覆盖(因为正文提到 NaN),但它没有解释为什么公共静态布尔运算符 == 被覆盖,因为该 = 的正文= 方法没有提到 NaN。我在问 == 方法。到目前为止,泰科布的理论似乎最强大。
  • @barlop,是的,你是对的。当我浏览 Jon Skeet 的这篇文章时:blogs.msdn.com/b/csharpfaq/archive/2004/03/29/…。它说:对于值类型,我通常使用 == 来获得更易于阅读的代码。如果值类型为 == 提供了与 Equals 不同的重载,事情会变得很棘手,但我认为这种类型一开始就设计得很糟糕。
  • @Matt 虽然如果 Double 没有重载 == 的情况(理论上),那么仍然存在 Equals 的行为与 == 不同的问题。因为 .Equals 被重载,而 == 没有被重载为等于 .Equals。就好像他们可能认为他们应该重载它以匹配所以他们使用了一些生成的代码但忘记了理智地实现 == (理智的存在,正如 Jon Skeet 爵士在你的报价中所说的那样,== 和 .Equals 的作用相同)。
【解决方案3】:

使用 ILSpy 我可以看到 Double.Equals 有一些额外的逻辑来检查被比较的值是否为 NaN。

ILSpy 还将== 的主体反编译为:

public static bool operator ==(double left, double right)
{
    return left == right;
}

其他运算符遵循相同的模式,这很奇怪。可能是反编译错误?

【讨论】:

  • 这里有源代码时无需使用 ILSpy referencesource.microsoft.com/#mscorlib/system/…
  • @Nighthawk441:在这种情况下没有,但总的来说 ILSpy 有一些有用的导航和分析功能。它也比使用网站更快。 :)
  • 你为什么写“其他运算符遵循相同的模式,这很奇怪。也许是反编译错误?”比如,你是什么意思?我猜你认为它看起来是多余的,因此它可能是反编译中的错误。看起来这不是反编译的错误,而是设计的错误。糟糕的设计。
  • 我也想知道如果执行该方法是否会无限递归?!
  • 没错。我怀疑我们正在研究一些特定于基本数据类型的硬编码 .Net 运行时行为。
猜你喜欢
  • 1970-01-01
  • 2010-10-17
  • 2017-05-08
  • 1970-01-01
  • 2014-12-13
  • 2012-05-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多