Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 256
Ankit Avatar asked Dec 25 '15 01:12

Ankit


3 Answers

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.

like image 101
Willem Van Onsem Avatar answered Sep 29 '22 14:09

Willem Van Onsem


It's because the System.Object implementation of == tests reference equality, like the static Equals(object, object), while instance Equals(object) is overloaded, so it checks the actual value.

When you box a value type twice, you get two different instances, so of course reference equality fails.

The operator, being static, is bound at compile time, so there is no dynamic dispatch. Even with strings, which are already reference types and are therefore not boxed when assigned to an object-type variable, you can get an unintended reference comparison with the == operator if one of the operands has a static type other than string.

like image 22
phoog Avatar answered Sep 29 '22 13:09

phoog


The operator == is like an overloaded static function selected based on the compile time types. In your case the type of the values is Object, for which the == operator implements reference equality.

On the other hand .Equals is virtual and overridden, so it will do the comparison based on the actual types.

like image 22
Tamas Hegedus Avatar answered Sep 29 '22 15:09

Tamas Hegedus