views:

59

answers:

2

I've wondered about this, so I figure I'll ask it.

Most places you'll see use the same semantic logic for overriding Equals as GetHashCode for memberwise equality...however they usually use different implementations:

    public override bool Equals(object obj)
    {
        if (obj == null || GetType() != obj.GetType())
        {
            return false;
        }
        var other = (MyType)obj;
        if (other.Prop1 != Prop1)
        {
            return false;
        }
        return true;
    }

    public override int GetHashCode()
    {
        int hash = -657803396;
        num ^= Prop1.GetHashCode();
        return num;
    }

If you're implementing memberwise equality for your type (lets say for storing in a dictionary), why not just override GetHashCode then do something like this for Equals:

    public override bool Equals(object obj)
    {
        return this.HashEqualsAndIsSameType(obj);
    }

    public static bool HashEquals(this object source, object obj)
    {
        if (source != null && obj != null)
        {
            return source.GetHashCode() == obj.GetHashCode();
        }
        if (source != null || obj != null)
        {
            return false;
        }
        return true;
    }

    public static bool HashEqualsAndIsSameType<T>(this T source, object obj)
    {
        return (obj == null || obj.GetType() == typeof(T)) && source.HashEquals(obj);
    }
+1  A: 

Hashes are not 1-to-1, you can have multiple different values that hash to the same value, but which should compare as not equal. So you cannot really implement Equals in terms of GetHashCode. This is why you have collisions in a hash table, and why a hash table lookup must involve call(s) to both GetHashCode and Equals.

Eloff
+5  A: 

Because there is a real risk of conflicts. Hash-codes are not unique. They can (when different) prove inequality, but never equality. When looking for an item:

  • get the hash-code(s)
  • if the hash-code is different, the object is different; discard it
  • if the hash-code is the same, check Equals:
  • if Equals reports true they are the same
  • else discard

Consider long... since hash-code is int, it is easy to see that there are lots and lots of conflicts.

Marc Gravell
So how would you recommend implementing a memberwise GetHashCode/Equals with the least amount of duplicated member references? Such duplications can lead to accidental ommissions and cause significant problems (which is what prompted my question today). Also, what if we do want a unique checksum for an object. Is there a good interface that exists already for defining that a type offers the ability to compute a checksum?
JeffN825
@jeff it is very rare you need to, but tools like resharper will do it for you
Marc Gravell
One example - offline optimistic caching and/or locking of objects of various types (in other words, there is no column on a table in a database to tag with a version id). The client and server both need a way of computing a hash or checksum (which would you call it) that is unique, so that if the client sends back a stale version to the server, the server knows not to save it and to throw an exception. How would you compute the hash/checksum for this purpose?
JeffN825