Equals vs GetHashCode when comparing objects
Asked Answered
I

3

18

Should we override both Equals and GetHashCode properties when implementing a custom class instances comparison?

In the following code I have a collection of classes. The class A is compared by the ID, the class B - by Code.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            List<I> bars = new List<I>();
            bars.Add(new A() { Id = 1, Code = "one A" });
            bars.Add(new B() { Id = 1, Code = "one B" });
            bars.Add(new A() { Id = 1, Code = "one A+" });
            bars.Add(new B() { Id = 1, Code = "one B" }); // Code = "one B+"

            var distictBars = bars.Distinct();

            foreach (var item in distictBars)
            {
                Debug.WriteLine(item.Code);
            }
        }
    }

    interface I
    {
        string Code { get; set; }
    }

    class A : I, IEquatable<A>
    {
        public int Id { get; set; }
        public string Code { get; set; }

        public bool Equals(A other)
        {
            // this is the ??? comparison
            return this.Id == other.Id;
            //return this.Code == other.Code;
        }

        public override bool Equals(object obj)
        {
            if (obj is A)
                return this.Equals(obj as A);
            else
                return object.ReferenceEquals(this, obj);
        }

        public override int GetHashCode()
        {
            // this is the wanted comparison
            return this.Id;
        }
    }

    class B : I, IEquatable<B>
    {
        public int Id { get; set; }
        public string Code { get; set; }

        public bool Equals(B other)
        {
            // this is the ??? comparison
            return this.Id == other.Id;
        }

        public override bool Equals(object obj)
        {
            if (obj is B)
                return this.Equals(obj as B);
            else
                return object.ReferenceEquals(this, obj);
        }

        public override int GetHashCode()
        {
            // this is the wanted comparison
            return this.Code.GetHashCode();
        }
    }
}

The output is:

one A
one B

in case the commented Code = "one B+" the output is

one A
one B
one B+

Now I ask myself what for should I override the Equals in the B class if it seems that this have no effect on comparison?

Is GetHasCode() overriding sufficient for that kind of comparisons?

Inshrine answered 12/5, 2011 at 16:30 Comment(1)
possible duplicate of Have I implemented Equals()/GetHashCode() correctly?Taxiway
I
30

Here's what you need to understand about the relationship between Equals and GetHashCode.

Hash codes are used by hashtables to quickly find a "bucket" in which an element is expected to exist. If elements are in two different buckets, the assumption is they cannot be equal.

The outcome of this is that you should view a hash code, for purposes of determining uniqueness, as a quick negative check: i.e., if two objects have different hash codes, they are not the same (regardless of what their Equals methods return).

If two objects have the same hash code, they will reside in the same bucket of a hash table. Then their Equals methods will be called to determine equality.

So GetHashCode must return the same value for two objects that you want to be considered equal.

Ilona answered 12/5, 2011 at 17:8 Comment(3)
Does that mean that different objects in the same bucket have the same HashCode?Troxler
@MohammedNoureldin exactly. If you insert N objects with keys that yield the same HashCode into a Dictionary they will end up in the same bucket, that is, when trying to get any of these objects by key, you will end up enumerating the whole bucket in order to get the corresponding element for the provided key.Massenet
That's not precisely true; different objects in the same bucket have either the same HashCode or one that's considered sufficiently equivalent to it by the collection (usually modulo the number of buckets), because there are usually more possible hash codes than there are possible buckets.Calefactory
C
8

The Distinct method will use the GetHashCode method to determine inequality between the items, and the Equals method to determine equality.

It first make a fast comparison using the hash code to determine which items are definitely not equal, i.e. have different hash codes, then it compares the items that have the same hash code to determine which are really equal.

In your implementation of the B class you have an inconsistent implementation of the GetHashCode and Equals method, so the comparison won't work properly. Your two B objects have different hash codes, so they won't be compared to each other. Two items that are considered to be equal should also return the same hash code.

If a class implements the IEquatable<T> interface, the Equals(T) method will be used, otherwise the Equals(object) method is used.

Calle answered 12/5, 2011 at 17:8 Comment(0)
A
5

You always need to override them together and with compatible implementations. A hash-code match/mismatch means (respectively) "possible equality" and "non-equality". The hash-code by itself does not indicate equality. Hence, after a hash-code match is found (or used to create groups of values), Equals is still checked to determine a match.

If the two do not agree, you might never find matches.

Agonic answered 12/5, 2011 at 17:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.