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

Why does == not work while comparing two object type variables boxed with same int value

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

想检查为什么会这样。

下面的代码片段是通用对象类型数据 属性

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)

与 temp.Data == 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 起作用。

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

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

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

运算符是静态的,在编译时绑定,因此没有动态调度。即使字符串已经是引用类型,因此在分配给对象类型变量时不会装箱,如果其中一个操作数的静态类型不是 string.

有两个原因:

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

    您可以在 .Equals vs == 的规格中阅读:

    By default, the operator == tests for reference equality by determining if two references indicate the same object, so reference types do not need to implement operator == in order to gain this functionality. When a type is immutable, meaning the data contained in the instance cannot be changed, overloading operator == to compare value equality instead of reference equality can be useful because, as immutable objects, they can be considered the same as long as they have the same value. Overriding operator == in non-immutable types is not recommended.

    Overloaded operator == implementations should not throw exceptions. Any type that overloads operator == should also overload operator !=.

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

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

  • 更多运算符在 编译时解析:

    以下面的csharp交互式shell程序为例:

    $ 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# 可以在编译时进行的唯一绑定是具有引用相等性的绑定。

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

另一方面,.Equals 是虚拟的和覆盖的,所以它会根据实际类型进行比较。