Friday, December 22, 2017

Overriding Equals

When you override Equal method in .Net it gives you warning that you have not override GetHashCode method.

class PhoneNumber
    {
        public String AreaCode { get; set; } = "2";
         public String Exchange { get; set; } = "45";

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (obj.GetType() != this.GetType()) return false;

            var phoneNum = obj as PhoneNumber;

            return this.AreaCode == phoneNum.AreaCode && this.Exchange == phoneNum.Exchange;
           
        }
    }

Although it is fine works well.

So Why .Net asking for overriding GetHashCode method let see

Suppose we have added our object in dictionary

Dictionary directory = new Dictionary();

directory.Add(
    new PhoneNumber { AreaCode = "123", Exchange = "456" },
    new Employee { FirstName = "Gordon", LastName = "Freeman" });

directory.Add(
    new PhoneNumber { AreaCode = "111", Exchange = "222" },
    new Employee { FirstName = "Gordon", LastName = "Freeman" });
    );

Lets try to pull our value


Employee employee = directory[new PhoneNumber { AreaCode = "123", Exchange = "456"}];
// Throws exception: "The given key was not present in the dictionary"


Also it would not throw an error while you will add same value object

Dictionary directory = new Dictionary();

directory.Add(
    new PhoneNumber { AreaCode = "123", Exchange = "456" },
    new Employee { FirstName = "Super", LastName = "Mario" });

directory.Add(
    new PhoneNumber { AreaCode = "123", Exchange = "456"},
    new Employee { FirstName = "Princess", LastName = "Peach" });

// No duplicate key exceptions thrown!


What is the Correct way to do that

Dictionary directory = new Dictionary();

PhoneNumber number = new PhoneNumber { AreaCode = "123", Exchange = "456"};

directory.Add(number, new Employee { FirstName = "Super", LastName = "Mario" });

Employee employee = directory[number]; // Works!

As these examples demonstrate, the Dictionary uses GetHashCode to quickly pull up a set of results from the collection. Once it has this set, then it can go through each one and verify equality by using Equals; calling Equals on a small set retrieved via a hash-based search is a lot more efficient than calling Equals on the whole lot.

If we haven’t implemented our own version of GetHashCode, then the default implementation of the method is used, which generates a hash specific to the instance of the object itself, rather than a hash based on the values within our class. To be able to properly use our PhoneNumber in a hash-based collections, we need the latter.

(You’ll note that this is analogous to how the default, reference equality implementation of Equals differs from our overridden, value equality implementation above)

Now that we’re convinced we need to override GetHashCode, let’s do it:

Override GetHashCode()

Here’s the golden rule for implementing GetHashCode (from MSDN):

If your overridden Equals method returns true when two objects are tested for equality, your overridden GetHashCode method must return the same value for the two objects.

Based on that, we can surmise that the properties of the class used in our implementation of Equals should make a reappearance in our implementation of GetHashCode. On top of that, we know that we need to return an int from GetHashCode.

Furthermore, because GetHashCode will be used to find objects within hash-based collections, our implementation of GetHashCode should meet two other qualifications: quick to compute with a relatively unique result.

If we have an implementation of GetHashCode that takes a long time, then we will severely impact the performance of any hash-based collection that uses our class.

Furthermore, recall from above that hash-based collections first use GetHashCode to get a smaller subset of items from a collection and then look through those objects with Equals. If our implementation often generates similar hashes, this will cause “collisions” (essentially, similar or identical hashes crowded together) in our hash-based collections, forcing these collections to constantly rifle through a large set of results from a GetHashCode search.

public override int GetHashCode()
{
    int hash = 13;
    hash = (hash * 7) + AreaCode.GetHashCode();
    hash = (hash * 7) + Exchange.GetHashCode();
    hash = (hash * 7) + SubscriberNumber.GetHashCode();
    return hash;
}


How did we end up with 7 and 13? Well, we were looking for prime numbers as this ensures our hash will produce evenly distributed numbers

To make it more accurate so that it can throw exception in case of overflow

public override int GetHashCode()
{
     unchecked
    {
        int hash = 13;
        hash = (hash * 7) + (!Object.ReferenceEquals(null, AreaCode) ? AreaCode.GetHashCode() : 0);
        hash = (hash * 7) + (!Object.ReferenceEquals(null, Exchange) ? Exchange.GetHashCode() : 0);
        hash = (hash * 7) + (!Object.ReferenceEquals(null, SubscriberNumber) ? SubscriberNumber.GetHashCode() : 0);
        return hash;
    }
}

http://www.loganfranken.com/blog/692/overriding-equals-in-c-part-2/

No comments:

Followers

Link