Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overridding Equals and GetHash

Tags:

c#

I have read that when you override Equals on an class/object you need to override GetHashCode.

 public class Person : IEquatable<Person>
    {
        public int PersonId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }

        public Person(int personId, string firstName, string lastName)
        {
            PersonId = personId;
            FirstName = firstName;
            LastName = lastName;

        }

        public bool Equals(Person obj)
        {
            Person p = obj as Person;

            if (ReferenceEquals(null, p)) 
                return false;
            if (ReferenceEquals(this, p)) 
                return true;

            return Equals(p.FirstName, FirstName) && 
                   Equals(p.LastName, LastName);
        }


    }

Now given the following:

 public static Dictionary<Person, Person> ObjDic= new Dictionary<Person, Person>();
 public static Dictionary<int, Person> PKDic = new Dictionary<int, Person>();

Will not overridding the GetHashCode affect both of the Dictionary's above? What I am basically asking is how is GetHashCode generated? IF I still look for an object in PKDic will I be able to find it just based of the PK. If I wanted to override the GetHashCode how would one go about doing that?

like image 857
Chris G Avatar asked Jun 23 '10 15:06

Chris G


2 Answers

You should always override GetHashCode.

A Dictionary<int, Person> will function without GetHashCode, but as soon as you call LINQ methods like Distinct or GroupBy, it will stop working.

Note, by the way, that you haven't actually overridden Equals either. The IEquatable.Equals method is not the same as the virtual bool Equals(object obj) inherited from Object. Although the default IEqualityComparer<T> will use the IEquatable<T> interface if the class implements it, you should still override Equals, because other code might not.

In your case, you should override Equals and GetHashCode like this:

public override bool Equals(object obj) { return Equals(obj as Person); }
public override int GetHashCode() {
    return FirstName.GetHashCode() ^ LastName.GetHashCode();
}
like image 166
SLaks Avatar answered Oct 26 '22 16:10

SLaks


In your scenario, not overriding GetHashCode on your type will affect only the first dictionary, as the key is what's used for hashing, not the value.

When looking for the presence of a key, the Dictionary<TKey,TValue> will use the hash code to find out if any keys could be equal. It's important to note that a hash is a value that can determine if two things could be equal or very likely are equal. A hash, strictly speaking cannot determine if two items are equal.

Two equal objects are required to return the same hash code. However, two non-equal objects are not required to return different hash codes. In other words, if the hash codes don't match, you're guaranteed that the objects are not equal. If the hash codes do match, then the objects could be equal.

Because of this, the Dictionary will only call Equals on two objects if their hash codes match.

As to "how to override GetHashCode", that's a complicated question. Clasically, a hashing algorithm should provide a balance between even distribution of the codes over the set of values with a low collision rate (a collision is when two non-equal objects produce the same code). This is a simple thing to describe and a very difficult thing to accomplish. It's easy to do one or the other, but hard to balance them.

From a practical perspective (meaning disregarding performance), you could just XOR all of the characters of the first and last names (or even use their respective hash codes, as Joel suggests) as your hash code. This will give a low degree of collision, but won't result in a terribly even distribution. Unless you're dealing with very large sets or very frequent lookups, it won't be an issue.

like image 22
Adam Robinson Avatar answered Oct 26 '22 14:10

Adam Robinson