Sunday, October 19, 2008

TDD, reflection and the PropertiesEqual extension method

In my last post I wrote about the extension method ForEach, which is a very simple but useful method, at least when it comes to readability.

In this post I'll try to explain another extension method, which I'll call PropertiesEqual. It's purpose is to extend the object class with a method to compare the properties of two objects:

public static bool PropertiesEqual<T>(this T obj1, T obj2)
{
return true if all properties of obj1 equals all properties of obj2
}

You probably know of the object.Equals method, which per default compares the memory addresses of two objects. That is, two objects are equal only if they are exactly the same object. If you really want to compare the content of two objects, you need to override this function in your class and manually compare them.

When I designed this function, I started with two simple test cases:
1. If two objects of the same class have the same public properties, yield true
2. If two objects of the same class have different public properties, yield false

Translated to code, this becomes:

class TestClass
{
public int A { get; set; }
public int B { get; set; }
}
[TestMethod()]
public void PropertiesEqualTest()
{
Assert.IsTrue(new Test { A = 1, B = 1 }.PropertiesEqual(new Test { A = 1, B = 1 }));
Assert.IsFalse(new Test { A = 1, B = 1 }.PropertiesEqual(new Test { A = 1, B = 0 }));
}

The implementation is very straight forward:

public static bool PropertiesEqual<T>(this T obj1, T obj2)
{
return typeof(T).GetProperties().All(property =>
{
var prop1 = property.GetValue(obj1, null);
var prop2 = property.GetValue(obj2, null);
return prop1.EqualsTo(prop2);
});
}

typeof(T).GetProperties() will return all public properties of T, and property.GetValue(obj, null) will return the value of a given property. The All extension method returns true only if all elements of the sequence satisfy the given condition, i.e. all properties are equal.

The test cases will pass fine, and we now have a simple way of comparing properties of two objects! In the next post I'll try to describe how to extend the method with support for recursive properties (compare properties of a property), and in the one after that I'll write about how to implement an IEqualityComparer based on this method.

EDIT: Note, these series aren't much of the type "here is a revolutionary new technique", but more of "here is how I would write the code for this". My focus is to get you to understand how I think when I design methods, not to tell you that "this is the way"!

5 comments:

Anonymous said...

I haven't look in the docs for property.GetValue, but wouldn't you want to use 'default' of the type for the property instead?

Christian Genne said...

Hm, not sure what you mean, but the docs says the following:

Returns the value of the property with optional index values for indexed properties.

The second parameter is thus the index value, which should be null for non indexed properties.

Anonymous said...

Ok Genne, never mind my comment.

But shouldn't I get a little creds for bringing up the idea of writing an equal method as extension methods, when we had sushi last?

Christian Genne said...

Didn't remember I had the idea from then, just thought that "writing a property equality extension method would be cool!". If I got the idea from you, which doesn't sound unlikely, then creds to you!

Why don't you write your own implementation so that we can compare each others?

Anonymous said...

Well actually we discussed extension methods in general, and I was looking for an example of them. I said an equal method, and has some, vague, ideas why it would be a good idea (forgot now :) But you argued against it pretty strong, and I was a bit surprised when you wrote an extension method for the object class that compared objects (although for properties).
So anyhow ... :-)