为什么 == 在比较两个用相同 int 值装箱的对象类型变量时不起作用

本文关键字:int 对象 不起作用 类型变量 比较 两个 为什么 | 更新日期: 2023-09-27 18:28:10

在尝试在 C# 中实现一个简单的单向链表时,我注意到在比较两个用 int 值装箱的对象类型变量时==不起作用,但.Equals工作。

想检查为什么会这样。

下面的代码片段是一个通用对象类型 Data 属性

public class Node {
    /// <summary>
    /// Data contained in the node
    /// </summary>
    private object Data { get; set; };
}

下面的代码遍历单向链表并搜索 object 类型的值 -

/// <summary>
/// <param name="d">Data to be searched in all the nodes of a singly linked list
/// Traverses through each node of a singly linked list and searches for an element
/// <returns>Node if the searched element exists else null </returns>
public Node Search(object d)
{
    Node temp = head;
    while (temp != null)
    {
        if (temp.Data.Equals(d))
        {
            return temp;
        }
        temp = temp.Next;
    }
    return null;
}

但是,如果我更换

temp.Data.Equals(d)

跟 临时。数据 == d

即使temp.Datad都具有值"3",它也会停止工作。==不适用于对象类型变量的任何原因?

这是 Main 函数的代码片段 -

SinglyLinkedList list = new SinglyLinkedList();
list.Insert(1);
list.Insert(2);
list.Insert(3);
list.Insert(4);
list.Insert(5);
list.Print();
Node mid = list.Search(3);

我相信由于我传递了一个 int 值3并且 Search 方法需要一个对象类型,因此它会成功地将 3 装箱为对象类型。但是,不知道为什么==不起作用,但.Equals起作用。

运算符是否仅针对值类型重载==

为什么 == 在比较两个用相同 int 值装箱的对象类型变量时不起作用

有两个原因:

  • Equals不受==限制,反之亦然,默认情况下检查引用相等性:

    正如您在.Equals==的规格中读到的那样:

    默认情况下,运算符==通过确定两个引用是否指示同一对象来测试引用相等性,因此引用类型无需实现运算符==即可获得此功能。当类型是不可变的(这意味着实例中包含的数据无法更改(时,重载运算符==来比较值相等而不是引用相等可能很有用,因为作为不可变对象,只要它们具有相同的值,就可以将它们视为相同。不建议重写不可变类型中的运算符==

    重载运算符==实现不应引发异常。任何重载运算符==的类型也应重载运算符!=

    尽管如果您不覆盖!=,编译器会抛出错误,并会警告您最好同时覆盖.Equals.GetHashCode

    所以覆盖/重载.Equals==!=是不同的事情。覆盖.Equals对重载==!=没有影响。毕竟==是自定义运算符。尽管这样做并不明智,但您可以将其用于相等性检查以外的其他目的。

  • 此外,运算符在编译时解析:

    以以下示例为例,以下csharp交互式外壳程序:

    $ csharp
    Mono C# Shell, type "help;" for help
    Enter statements below.
    csharp> public class Foo {
          >  
          > public int data;
          >  
          > public static bool operator == (Foo f1, Foo f2) {
          >     return f1.data == f2.data;
          > }
          >  
          > public static bool operator != (Foo f1, Foo f2) {
          >  
          >     return f1.data != f2.data;
          > }
          >  
          > }
    (1,15): warning CS0660: `Foo' defines operator == or operator != but does not override Object.Equals(object o)
    (1,15): warning CS0661: `Foo' defines operator == or operator != but does not override Object.GetHashCode()
    csharp> object f = new Foo();
    csharp> object f2 = new Foo();
    csharp> f == f2
    false
    csharp> Foo f3 = f as Foo;
    csharp> Foo f4 = f2 as Foo;
    csharp> f3 == f4
    true
    

    如您所见,如果将对象称为 objectFoo==会给出不同的结果。由于你使用object,C# 在编译时唯一可以做的绑定是具有引用相等的绑定。

这是因为==测试的System.Object实现引用相等性,如静态Equals(object, object),而实例Equals(object)是重载的,所以它会检查实际值。

当你对一个值类型进行两次装箱时,你会得到两个不同的实例,所以引用相等当然会失败。

运算符是静态的,在编译时绑定,因此没有动态调度。 即使对于字符串,这些字符串已经是引用类型,因此在分配给对象类型变量时不会装箱,如果其中一个操作数具有 string 以外的静态类型,则可能会与 == 运算符进行意外的引用比较。

运算符==就像根据编译时类型选择的重载静态函数。在您的情况下,值的类型是 Object==运算符为此实现引用相等。

另一方面,.Equals是虚拟的和被覆盖的,因此它将根据实际类型进行比较。