Friday, May 9, 2008

Equals and GetHashCode The Right Way!!!!!

Hi
Recently I witness many mistakes when creating new class. many developers override Object.GetHashCode() in a wrong way!!! that can make error that will be very hard to find. Lets start with the basic rules when writing your own GetHashCode:
  • If X.Eqauls(Y) == true then X.GetHashCode() == Y.GetHashCode(). if two objects are equals they must have the same HashCode.
  • If X.GetHashCode() != Y.GetHashCode() then X.Equals(Y) == false. If two objects has defraent hash code they cant be Equal to each other.
  • object GetHashCode() must NOT be changed during it life cycle. Once the object was created it hash code must NOT change!!!
  • If X.Eqauls(Y) == false then X.GetHashCode() Should (NOT A MUST) be defrant from Y.GetHashCode()
If you creates your own object and overrides Equals you will get an Warning:
Here is an example of a wrong way to make HashCode and what will happen:

class
MyWrongObject
    {
private int m_One;
public int One
{
get { return m_One; }
set { m_One = value; }
}
public MyWrongObject() { }
public MyWrongObject(int one)
{
m_One = one;
}

public override bool Equals(object obj)
{
MyWrongObject other = obj as MyWrongObject;
if (other != null)
return (One == other.One);
else
return false;
}

public override string ToString()
{
return String.Format("MyWrongObject[{0}]", One);
}

public override int GetHashCode()
{
return ToString().GetHashCode();
}


public static void Main(string[] args)
{
HashSet<MyWrongObject> set1 = new HashSet<MyWrongObject>();
MyWrongObject obj1 = new MyWrongObject(1, 1);
set1.Add(obj1);
if (set1.Contains(obj1))
Console.WriteLine("Found obj1");
obj1.One = 2; // This line changes the GetHashCode() of obj1
if (set1.Contains(obj1))
{
// We wont see this line because Contains looks at the GetHashCode()
then at Equals()

Console.WriteLine("You won't see this line");
}
else
Console.WriteLine("Hmmm.. can't find obj1 but obj1 is here");
foreach (MyWrongObject obj in set1)
{
if (obj1.Equals(obj))
Console.WriteLine("obj:{0} Can't find obj1 but obj1 is here", obj);
}
}
}



Please notice that you can't make a setter to a member that changes the GetHashCode() of the object.
If you do that you should think about throwing an exeption when using GetHashCode()

Enjoy,

1 comment:

david santos said...

I loved this post and this blog.
Have a nice weekend