says: "mastering the skills to maximize the tools"

C# Generic Comparer

April 17th, 2008

This morning I ran into a situation where i need to sort an array of ServiceController before binding into a combobox. So I looked around and I found Array.Sort (or List.Sort). Tried with the parameter-less call, but .NET doesn’t like it. One of the required overloaded methods requires a IComparer. Hmm, looks familiar and found some straightforward examples online. Immediately I have a simple class that solves my problem.

private class ServiceControllerSort : IComparer<ServiceController>
{
	public int Compare(ServiceController x, ServiceController y)
	{
		return string.Compare(x.DisplayName, y.DisplayName, true);
	}
}

Now what? What if next time I want to sort on my ExtendedServiceController class (as if there is necessity to do one)? I will have more and more IComparer implementations. I decided to explore further and this page says it all. Took the code, clean it up and woots, I have a generic comparison class that is usable for almost all types.

/// <summary>
/// Custom sorter based on property
/// </summary>
/// <typeparam name="T">Target Type</typeparam>
public class ExtSortComparer<T>: IComparer<T>
{
	private ListSortDirection _sortDirection = ListSortDirection.Ascending;
	private PropertyDescriptor _targetPropDescriptor;
 
	/// <summary>
	/// Constructor
	/// </summary>
	public ExtSortComparer()
	{
	}
 
	/// <summary>
	/// Constructor
	/// </summary>
	/// <param name="PropertyName">Name of property to sort on. Case sensitive.</param>
	public ExtSortComparer(string PropertyName)
	{
		_targetPropDescriptor = TypeDescriptor.GetProperties(typeof(T))[PropertyName];
	}
 
	/// <summary>
	/// Constructor
	/// </summary>
	/// <param name="PropertyName">Name of property to sort on. Case sensitive.</param>
	/// <param name="SortDirection">Sort direction</param>
	public ExtSortComparer(string PropertyName, ListSortDirection SortDirection) : this(PropertyName)
	{
		_sortDirection = SortDirection;
	}
 
	/// <summary>
	/// Constructor
	/// </summary>
	/// <param name="TargetProperty">Descriptor of property to sort on</param>
	public ExtSortComparer(PropertyDescriptor TargetProperty)
	{
		_targetPropDescriptor = TargetProperty;
	}
 
	/// <summary>
	/// Constructor
	/// </summary>
	/// <param name="TargetProperty">Descriptor of property to sort on</param>
	/// <param name="SortDirection">Sort direction</param>
	public ExtSortComparer(PropertyDescriptor TargetProperty, ListSortDirection SortDirection)
	{
		_targetPropDescriptor = TargetProperty;
		_sortDirection = SortDirection;
	}
 
	/// <summary>
	/// Compare two given object
	/// </summary>
	/// <param name="x">Object A</param>
	/// <param name="y">Object B</param>
	/// <returns>Result of comparison</returns>
	public int Compare(T x, T y)
	{
		//ensure there is proper property description
		if (_targetPropDescriptor != null)
		{
			int intRetValue = _CompareValues(_targetPropDescriptor.GetValue(x), 
											_targetPropDescriptor.GetValue(y));
 
			//reverse the sorting if descending
			if (_sortDirection == ListSortDirection.Descending)
				return intRetValue * -1;
 
			return intRetValue; //return the result
		}
 
		return 0; //return neutral if insufficient information to sort
	}
 
	/// <summary>
	/// Compare values of target property
	/// </summary>
	/// <param name="ValueX">Value of property (Object A)</param>
	/// <param name="ValueY">Value of property (Object B)</param>
	/// <returns>Result of comparison</returns>
	private int _CompareValues(object ValueX, object ValueY)
	{
		if (ValueX is IComparable) //if X can be compared
			return ((IComparable)ValueX).CompareTo(ValueY);
 
		if (ValueY is IComparable) //else if Y can be compared
			return ((IComparable)ValueY).CompareTo(ValueX);
 
		//when both can't be compared, resort to string value.
		return ValueX.ToString().CompareTo(ValueY.ToString());
	}
}

The comments should explain what is happening internally. Another good addition to my common library! If you have any thoughts, shoot!

Post a Comment