Search code examples
c#equalitysingly-linked-listequals-operator

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


While trying to implement a simple singly linked list in C#, I noticed that == does not work while comparing two object type variables boxed with an int value but .Equals works.

Wanted to check why that is so.

The below snippet is a generic object type Data property

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

The below code traverses the singly linked list and searches for a value of type 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;
}

However, if I replace

temp.Data.Equals(d)

with temp.Data == d

it stops working even though temp.Data and d both have the value '3'. Any reasons why == does not work on object type variables?

Here's the snippet from the Main function -

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

I believe since I am passing an int value 3 and the Search method expects an object type, it would have successfully boxed 3 as a object type. However, not sure why == doesn't work but .Equals does.

Is == operator overloaded for value types only?


Solution

  • There are two reasons:

    • Equals is not bounded with == and vice versa, and by default checks for reference-equality:

      As you can read in the specifications of .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 !=.

      Although the compiler will throw an error if you do not override the != as well, and will warn that you better override both .Equals and .GetHashCode.

      So overriding/overloading the .Equals, == and != are different things. Overriding .Equals has no effect on overloading == and !=. After all the == is a custom operator. Although it is not wise to do so, you could use it for another purpose than an equality check.

    • Furthermore operators are resolved at compile-time:

      Take the following example the following csharp interactive shell program:

      $ 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
      

      As you can see, == gives a different result if you call the objects as object, or as Foo. Since you use an object, the only binding at compile time C# can make is the one with reference equality.