In this MSDN page it says:
Warning:
If you override the GetHashCode method, you should also override Equals, and vice versa. 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.
I have also seen many similar recommendations and I can understand that when overriding the Equals method I would also want to override the GetHashCode. As far as I can work out though, the GetHashCode is used with hash table look-ups, which is not the same as equality checking.
Here is an example to help explain what I want to ask:
public class Temperature /* Immutable */
{
public Temperature(double value, TemperatureUnit unit) { ... }
private double Value { get; set; }
private TemperatureUnit Unit { get; set; }
private double GetValue(TemperatureUnit unit)
{
/* return value converted into the specified unit */
}
...
public override bool Equals(object obj)
{
Temperature other = obj as Temperature;
if (other == null) { return false; }
return (Value == other.GetValue(Unit));
}
public override int GetHashCode()
{
return Value.GetHashCode() + Unit.GetHashCode();
}
}
In this example, two Temperature objects are considered equal, even if they are not storing the same things internally (e.g. 295.15 K == 22 Celsius). At the moment the GetHashCode method will return different values for each. These two temperatures objects are equal but they are also not the same, so is it not correct that they have different hash codes?
When storing a value in a hash table, such as Dictionary<>
, the framework will first call GetHashCode()
and check if there's already a bucket in the hash table for that hash code. If there is, it will call .Equals()
to see if the new value is indeed equal to the existing value. If not (meaning the two objects are different, but result in the same hash code), you have what's known as a collision. In this case, the items in this bucket are stored as a linked list and retrieving a certain value becomes O(n).
If you implemented GetHashCode()
but did not implement Equals()
, the framework would resort to using reference equality to check for equality which would result in every instance creating a collision.
If you implemented Equals()
but did not implement GetHashCode()
, you might run into a situation where you had two objects that were equal, but resulted in different hash codes meaning they'd maintain their own separate values in your hash table. This would potentially confuse anyone using your class.
As far as what objects are considered equal, that's up to you. If I create a hash table based on temperature, should I be able to refer to the same item using either its Celsius or Fahrenheit value? If so, they need to result in the same hash value and Equals()
needs to return true.
Update:
Let's step back and take a look at the purpose of a hash code in the first place. Within this context, a hash code is used as a quick way to identify if two objects are most likely equal. If we have two objects that have different hash codes, we know for a fact they are not equal. If we have two objects that have the same hash code, we know they are most likely equal. I say most likely because an int can only be used to represent a few billion possible values, and strings can of course contain the complete works of Charles Dickens, or any number of possible values. Much in the .NET framework is based on these truths, and developers that use your code will assume things work in a way that is consistent with the rest of the framework.
If you were to have two instances that have different hash codes, but have an implementation of Equals()
that returns true, you're breaking this convention. A developer that compares two objects might then use one of of those objects to refer to a key in a hash table and expect to get an existing value out. If all of a sudden the hash code is different, this code might result in a runtime exception instead. Or perhaps return a reference to a completely different object.
Whether 295.15k and 22C are equal within the domain of your program is your choice (In my opinion, they are not). However, whatever you decide, objects that are equal must return the same has code.