A Generic Property Sorter
Posted on 6/12/2007
When I switched to C# after nearly 18 years of coding with C++, I missed a few things. Templates were one of those features that I found hard to live without. As a C++ programmer, I used the Standard Template Library (STL) a lot. And I wrote a lot of my own generic algorithms and classes using C++ templates. So when I started using C# in 2001, it felt odd not to have a templating mechanism in my language. I didn't get all of the capabilities of templates back with C# generics, but it's close enough. In this article, I'll show how to implement an Icomparer<T> algorithm that uses reflection to sort a collection of objects based on property names.
I recently built the class below using C# generics as a demonstration to my co-workers. The idea was to create a generic IComparer that can compare any type of objects based on a property that they share. With C# generics, the concept is simple: create a class that takes one or more types between the < and > symbols in the class name. The compiler will instantiate a version of the class for the type when you reference it. Check out the PropertyComparer defined below.
public class PropertyComparer<T> : IComparer<T>
{
private PropertyInfo m_sortProperty = null;
private int m_orderFactor = 1;
public PropertyComparer( string propertyName )
{
// get the information for the selected property
Type t = typeof( T );
m_sortProperty = t.GetProperty( propertyName );
}
public PropertyComparer( string propertyName,
System.Windows.Forms.SortOrder order ) :
this( propertyName )
{
// save the sort direction
if (order == System.Windows.Forms.SortOrder.Descending)
m_orderFactor = -1;
}
public int Compare( T x, T y )
{
// make sure there's a comparable field
if ((object)m_sortProperty == null)
return 0;
// both objects must exist
if ((object)x == null ||
(object)y == null)
return 0;
// get the comparing object and the comparand
Icomparable comparer =
m_sortProperty.GetValue( x, null ) as Icomparable;
object comparand = m_sortProperty.GetValue( y, null );
// make sure the comparer implements Icomparable
if ((object)comparer == null)
return 0;
// do the comparison
return comparer.CompareTo( comparand ) * m_orderFactor;
}
}
This would be an average sort of IComparer except as a generic algorithm, it uses the type (called T) for which it has been instantiated and a named property of that type with which to do the comparisons. When the comparer is instructed to compare two objects, it uses reflection to obtain the value of the named property for both comparands and compares them to obtain the result. So this little class can be used to compare and sort anything that exposes properties. Sound useful?
Imagine that you have a class called Product that exposes a property called Description. If you had a collection of Products in a shopping cart called productsInCart, using the PropertyComparer above you could sort them by the Description property with this little bit of code:
productsInCart.Sort(
new PropertyComparer<Products>( "Description" ) );
If you find yourself writing the same code over and over and you can't solve the problem with inheritence, think about using generics to code it once and reuse it. Less code means fewer bugs and generics are a great way to write less code.