Generic Sorting Using Reflection in .NET
By Jon Wojtowicz

  Download Source Code
Microsoft's .Net provides sorting on ArrayLists and arrays using a general purpose QuickSort algorithm.  To make the sorting work, the Sort methods use the System.IComparable interface to make the comparisons.  This is implemented in the basic types such as string, int, DateTime, etc.
What about my custom objects?  The Sort methods are overloaded and can take a System.IComparer.  By implementing this interface in a class we can provide a means of sorting our objects.  Since the properties of an object represent available data, we should be able to sort on any property.
Sorting collections of custom objects or arrays of "stuff" is tedious, boring code that gets in the way of all the fun tasks in programming.  How many time have you had spend hours writing code similar to Listing 1?  This code is also limiting in the manner in which you can sort.  What if my classes were structured as in List 2 and I wanted to sort by the Zip in the Address property?  With the implementation presented, you'd be writing a lot of IComparers to satisfy all the possible combinations of sorting a client could ask for.
You might just want to sort coming from the database.  This poses it's own set of special problems such as how do you dynamically create the ORDER BY clause.  Not to mention having to extract the data from the database every time the user decides to change the sort order.
Listing 1.
 
	public class PersonCollection : CollectionBase
	{
		public enum SortField
		{
			FirstName,
			LastName
		}

		...
		
		public void Sort(SortField sortField)
		{
			IComparer comparer = null;
			switch(sortField)
			{
				case SortField.FirstName:
					comparer = new FirstNameComparer();
					break;
				case SortField.LastName:
					comparer = new LastNameComparer();
					break;
			}
			InnerList.Sort(comparer);
		}

		...

		private class FirstNameComparer : IComparer
		{
			public int Compare(object x, object y)
			{
				Person p1 = (Person)x;
				Person p2 = (Person)y;
				return p1.FirstName.CompareTo(p2.FirstName);
			}
		}

		private class LastNameComparer : IComparer
		{
			public int Compare(object x, object y)
			{
				Person p1 = (Person)x;
				Person p2 = (Person)y;
				return p1.LastName.CompareTo(p2.LastName);
			}
		}


	}   
Listing 2.
 
	public class Person
	{
		private string firstName;
		private string lastName;
		private StreetAddress address;

		public Person(string firstName, string lastName)
		{
			this.firstName = firstName;
			this.lastName = lastName;
		}

		public string FirstName
		{
			get{return firstName;}
		}

		public string LastName
		{
			get{ return lastName; }
			set{ lastName = value; }
		}

		public StreetAddress Address
		{
			get{ return address; }
			set{ address = value; }
		}
	}
	
	public class StreetAddress
	{
		private string street;
		public string city;
		public string state;
		public string zip;

		public StreetAddress()
		{}

		public override string ToString()
		{
			return street + ", " + city + ", " + state + " " + zip;
		}

		public StreetAddress(string street, string city, string state, string zip)
		{
			this.state = state;
			this.street = street;
			this.city = city;
			this.zip = zip;
		}

		public string Street
		{
			get{return street;}
			set{street = value;}
		}

		public string City
		{
			get{return city;}
			set{city = value;}
		}

		public string State
		{
			get{return state;}
			set{state = value;}
		}

		public string Zip
		{
			get{return zip;}
			set{zip = value;}
		}
	}
   
.Net Reflection
Reflection is a powerful feature in .Net.  Basically it allows your code to look back at classes, objects, methods and properties at run time.  .Net has made these features easy to use API in the System.Relection namespace.  There are a lot of good resources for reflection on the internet and I'll only cover the parts I needed for my implementation.
PropertyComparer
The goal of the PropertyComparer was to eliminate the need to create custom IComparers for every class and every property.  I wanted it to be flexible enough to be able to sort by any property that implements IComparable.  I also wanted to be able to sort by properties in contained objects exposed as properties.  I decided to implement this using the doted notation used for referencing class properties.  Using Listing 2, if I wanted to sort the collection based on the Zip property of the Address property in the Person class, the sort field would be "Address.Zip".
Since I was using reflection which is a performance hit, I decided to make the PropertyComparer fixed on a specific property at creation.  This means that I would collect the PropertyDescriptorsCollection during creation.  I walked through the doted notation and collected all the PropertyDescriptorsCollections for the contained objects as well. the constructor and supporting method is in Listing 3.
Listing 3.
 
		public enum CompareOrder
		{
			Ascending,
			Descending
		}
	
		public PropertyComparer( string compareField, Type typeToCompare)
		{
			this.compareField = compareField;
			GetPropertyDescriptors(compareField,TypeDescriptor.GetProperties(typeToCompare));
			compareOrder = CompareOrder.Ascending;
		}
		
		public PropertyComparer( string compareField, Type typeToCompare, CompareOrder compareOrder)
		{
			this.compareField = compareField;
			GetPropertyDescriptors(compareField,TypeDescriptor.GetProperties(typeToCompare));
			this.compareOrder = compareOrder;
		}

		private void GetPropertyDescriptors(string propName, PropertyDescriptorCollection props)
		{
			propList.Add(propName, props);
			int point = propName.IndexOf(".");
			if(point > -1)
			{
                          string name = propName.Substring(0, point);	
                          PropertyDescriptor prop = props.Find(name, true);
                          if( prop == null )
                          throw new ArgumentOutOfRangeException("Could not find property " + name + ".");
                          GetPropertyDescriptors(propName.Substring(point + 1),
                              TypeDescriptor.GetProperties((prop.PropertyType)));
			}

		}
   
The Compare method is fairly small.  Most of the work was accomplished in a method called FindAndCompareProperty.  This method can be seen in listing 4.  The code recursively walks the dotted notation getting the PropertyDescriptorsfor each type specified from the internal list built in the constructor.  It the uses reflection to obtain the value of the property on each pass.  When it gets to the end of the dotted list it casts the object to IComparable and calls the compareTo method on the first object.
Listing 4.
		
		private int FindAndCompareProperty(string propName, object x, object y)
		{
			if (x == null && y == null)
			{
				return 0;
			}
			if (x == null)
			{
				return -1;
			}
			if (y == null)
			{
				return 1;
			}
			PropertyDescriptorCollection props = (PropertyDescriptorCollection)propList[propName];
			int point = propName.IndexOf(".");
			if(point == -1)
			{
				PropertyDescriptor prop = props.Find(propName, true);
				IComparable compare = prop.GetValue(x) as IComparable;
				if(compare == null)
					throw new ArgumentException("Property does not implement IComparable.");
				return compare.CompareTo(prop.GetValue(y));
			}
			else
			{
				string name = propName.Substring(0, point);
				PropertyDescriptor prop = props.Find(name, true);
				return FindAndCompareProperty(propName.Substring(point + 1), 
                                                               prop.GetValue(x), prop.GetValue(y));
			}
			
		}
		
Sorting With the PropertyComparer Class
We build an array of Person objects and sort based on the FirstName property using the code in Listing 5 .
Listing 5.
						
		string[] firstNames = {"Fred", "Barney", "Wilma", "Betty", "BamBam", "George"};
		string[] lastNames = {"Flintstone", "Rubble", "Flintstone", "Rubble", "Rubble", "Slate"};
		string[] streets = {"1313 Mockingbird Lane", "503 Fifth St.", "1 Rockingham Lane", "1 Fountain Square", 
                                "1 Dependability Square", "8966 E. 23rd Ave. N."};
		string[] cities={"Bedrock", "QuarryStone", "Rock Vegas", "SandStone", "Granite Beach", "Mica Hills"};
		string[] states={"FL", "TN", "KY", "VT", "IA", "NV"};
		string[] zips = {"61109", "01020", "50811", "41042", "90210", "37420"};
		
		Person[] people = new Person[6];
		for(int i = 0; i < 6; ++i)
		{
			people[i] = new Person(firstNames[i], lastNames[i]);
			people[i].Address = new StreetAddress(streets[i], cities[i], states[i], zips[i]);
		}

		Array.Sort(people, new PropertyComparer("FirstName", typeof(Person), CompareOrder.Ascending));

		
We can also sort based on the Zip property of the contained StreetAddress objects.  To do this we would change the Sort() to the following.
				
 Array.Sort(people, new PropertyComparer("Address.Zip", typeof(Person), CompareOrder.Ascending));
				
				
Performance
Since this class uses reflection we expect a performance hit.  It is typically 10X slower than having a plain comparer.  The time to sort is directly proportional to the depth of the sorting on dotted notation, i.e. sorting on Address.Street takes about twice as long as sorting on FirstName. the trade-off for the performance is the simplicity of coding sorting of any array or collection of custom objects.  If you need very high performance then this approach may not be suitable for you application.
Conclusion
Reflection is very powerful but relatively slow.  The concept of the PropertyComparer can be applied to any comparisons, not only sorting.  In the code example I've included a SortableCollectionBase class that allows performing an IndexOf by specifying the field to match.  This can also be applied to filtering by return a new list of matching objects.  Take a look at the code and imagine the possibilities.
Last Update: 6/25/2005