Overload operators the right way in C#

by Mads Kristensen 29. August 2006 03:12

It’s a good rule of thumb to overload the equality operators on classes. That ensures a correct comparison between to class instances of the same type. If you don’t, .NET automatically uses reflection and that is way slower than a custom implementation.

We all use the equality operators (“==”, “!=”) all the time and we expect them to be right every time. They are not!, but even if they were there is a good chance that you want to change them anyway. For instance, if your class has a unique Id property, then that property is the one that tells if two instances are the same. If you don’t overload the operators, you could have two instances with the same Id treated as two different instances.

First of all, you have to find out what makes two instances different. Let’s keep using the Id property example in an imaginary class called Foo. Then lets start overriding the GetHashCode() method so it returns the hashcode of the Id property.

/// <summary>

/// A uniquely key to identify this particullar instance of the class

/// </summary>

/// <returns>A unique integer value</returns>

public override int GetHashCode()

{

  return this.Id.GetHashCode();

}

Whenever the GetHashCode() method is called, it returns a unique integer value that represents the uniqueness of the class instance. Now we can use that method to overload the operators.

/// <summary>

/// Checks to see if two business objects are the same.

/// </summary>

public static bool operator ==(Foo first, Foo second)

{

  // Checks if the pointer in memory is the same

  if (Object.ReferenceEquals(first, second))

  {

    return true;

  }

 

  // Checks for null values

  if ((object)first == null || (object)second == null)

  {

    return false;

  }

 

  return first.GetHashCode() == second.GetHashCode();

}

 

/// <summary>

/// Checks to see if two business objects are different.

/// </summary>

public static bool operator !=(Foo first, Foo second)

{

  return !(first == second);

}

The reason why it is necessary to box the two Foo parameters (first, second) to type object, is to avoid an overflow exception.
 
We still need one more thing to be sure that we can compare two instances to each other, and that is by overriding the Equals() method. You can use the Equals() method or the operators to compare instances and they should always return the same result.

/// <summary>
///
Comapares this object with another
///
</summary>
///
<param name="obj">The object to compare</param>
///
<returns>True if the two objects as equal</returns>
public
override bool Equals(object obj)
{
  if (obj == null)
  {
    return false;
  }

  if (obj.GetType() == this.GetType())
  {
    return obj.GetHashCode() == this.GetHashCode();
 
}

  return false;
}

You can implement these methods on a base class so you won’t have to write it on all the derived classes. Because these methods uses GetHashCode() to compare two instances, it would be preferable to override that method in the derived classes if needed.

* Only $4.95/month ASP.NET & Windows 2008 + IIS 7 Hosting! FREE SQL Included

Tags:

Server-side

Comments

8/29/2006 8:45:58 PM #

 Michal Talaga

Overriding GetHashCode method is like walking a minefield. If you want to do it right, you have to follow the guidelines provided by MS. The problem with those guidelines is that they change every now and then. The most current version available (that is I think it is the most current) can be found here:
msdn2.microsoft.com/.../...object.gethashcode.aspx
Do some search on the issue of overloading Equls and GetHashCode and you will see Smile

Michal Talaga |

8/29/2006 9:33:06 PM #

 Thomas Gravgaard

Thia is a surprisingly tricky subject where there are more than one way to do it properly... Your method is good save for one issue that struck me. In your equals method you do not check if the objects are of the same type. So you would actually have a Customer with Id 42 be equal to a Product with Id 42. You should check for type equality as well.

Thomas Gravgaard |

8/30/2006 1:30:27 AM #

Josh

Thomas: The parameters are typed "Foo," so you couldn't pass a Customer in where Foo was expected. Derived classes could be passed in though, which may or may not mess things up.

I also wanted to make a note that I know that you used the GetHashCode method as an example, but people should be aware that hashcodes are not guaranteed (or even expected) to be unique.

Josh |

8/30/2006 6:49:29 AM #

 Damien Guard

I have to agree with Josh here - relying on the hashcode to be equal between two objects is suicidal and as Thomas points out Equals needs a type check too.

Otherwise you get the following;

int customerId = 5;
Product myProduct();
myProduct.Id = 5;

if (myProduct.Equals(customerId)) ....

will evaluate false which is obviously wrong.

[)amien

Damien Guard |

8/30/2006 3:13:52 PM #

 Mads Kristensen

Hi guys. Thanks for the input. I must have been sleeping in class. I've added the type check in the Equals() method as you suggested. I wen't back to some C# projects I've recently did and there the type check was actually implementet. I guess I wrote this post a little to fast.

Mads Kristensen |

11/20/2007 7:57:53 PM #

Pedram

If the hashcodes of 2 objects of the same type are equal, it doesn't mean that these objects are equal!

Pedram United Kingdom |

Comments are closed

About the slave

Mads Kristensen Mads Kristensen
Web developer at ZYB and founder of BlogEngine.NET. More...

LinkedIn ZYB Facebook Last.fm Twitter View Mads Kristensen's profile on Technorati

The Lounge

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2008