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

时间:2015-12-25 01:25:38

标签: c# equality singly-linked-list equals-operator

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

想要检查为什么会这样。

以下代码段是通用对象类型数据属性

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

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

/// <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都具有值&#39; 3&#39;它也会停止工作。 ==对对象类型变量不起作用的原因是什么?

这是主要功能的片段 -

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不起作用。

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

3 个答案:

答案 0 :(得分:10)

有两个原因:

  • Equals不与==绑定,反之亦然,默认情况下会检查引用相等性:

    正如您可以阅读.Equals vs ==的规范:

      

    默认情况下,运算符==通过确定两个引用是否指示相同的对象来测试引用相等性,因此引用类型不需要实现运算符==以获得此功能。当一个类型是不可变的,意味着实例中包含的数据不能被改变时,重载运算符==来比较值相等而不是引用相等可能是有用的,因为作为不可变对象,只要它们可以被认为是相同的它们具有相同的价值。不推荐在非不可变类型中覆盖运算符==

         

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

    虽然如果你没有覆盖!=,编译器也会抛出错误,并且会警告你最好覆盖.Equals.GetHashCode

    所以覆盖/重载.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
    

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

答案 1 :(得分:5)

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

当您将值类型两次打包时,会得到两个不同的实例,因此参考相等性当然失败。

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

答案 2 :(得分:4)

运算符==类似于基于编译时类型选择的重载静态函数。在您的情况下,值的类型为Object==运算符为其实现引用相等。

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