Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are you expected to override GetHashCode and Equals when overloading the equality operator?

Failing to override GetHashCode and Equals when overloading the equality operator causes the compiler to produce warnings. Why would it be a good idea to change the implementation of either? After reading Eric Lippert's blog post on GetHashCode it's seems like there probably aren't many useful alternatives to GetHashCode's base implementation, why does the compiler I encourage you to change it?

like image 294
evanmcdonnal Avatar asked May 20 '13 18:05

evanmcdonnal


3 Answers

Let's suppose you are implementing a class.

If you are overloading == then you are producing a type that has value equality as opposed to reference equality.

Given that, now the question is "how desirable is it to have a class that implements reference equality in .Equals() and value equality in ==?" and the answer is "not very desirable". That seems like a potential source of confusion. (And in fact, the company that I now work for, Coverity, produces a defect discovery tool that checks to see if you are confusing value equality with reference equality for precisely this reason. Coincidentally I was just reading the spec for it when I saw your question!)

Moreover, if you are going to have a class that implements both value and reference equality, the usual way to do it is to override Equals and leave == alone, not the other way around.

Therefore, given that you have overloaded ==, it is strongly suggested that you also override Equals.

If you are overriding Equals to produce value equality then you are required to override GetHashCode to match, as you know if you've read my article that you linked to.

like image 199
Eric Lippert Avatar answered Oct 27 '22 11:10

Eric Lippert


If you don't override Equals() when you override == you will have some amazingly bad code.

How would you feel about this happening?

if (x == y)
{
   if (!x.Equals(y))
       throw new InvalidOperationException("Wut?");
}

Here's an example. Given this class:

class Test
{
    public int Value;
    public string Name;

    public static bool operator==(Test lhs, Test rhs)
    {
        if (ReferenceEquals(lhs, rhs))
            return true;

        if (ReferenceEquals(lhs, null) || ReferenceEquals(rhs, null))
            return false;

        return lhs.Value == rhs.Value;
    }

    public static bool operator!=(Test lhs, Test rhs)
    {
        return !(lhs == rhs);
    }
}

This code will behave oddly:

Test test1 = new Test { Value = 1, Name = "1" };
Test test2 = new Test { Value = 1, Name = "2" };

if (test1 == test2)
    Console.WriteLine("test1 == test2"); // This gets printed.
else
    Console.WriteLine("test1 != test2");

if (test1.Equals(test2))
    Console.WriteLine("test1.Equals(test2)");
else
    Console.WriteLine("NOT test1.Equals(test2)"); // This gets printed!

You do NOT want this!

like image 20
Matthew Watson Avatar answered Oct 27 '22 10:10

Matthew Watson


My guess is that the compiler takes its clues from your actions, and decides that since you find it important to provide an alternative implementation of the equality operator, then you probably want the object equality to remain consistent with your new implementation of ==. After all, you do not want the two equality comparisons to mean drastically different things, otherwise your program would be hard to understand even on a very basic level. Therefore, the compiler thinks that you should redefine Equals as well.

Once you provide an alternative implementation Equals, however, you need to modify GetHashCode to stay consistent with the equality implementation. Hence the compiler warns you that your implementation might be incomplete, and suggests overriding both Equals and GetHashCode.

like image 42
Sergey Kalinichenko Avatar answered Oct 27 '22 11:10

Sergey Kalinichenko