Click here to Skip to main content
Click here to Skip to main content

Dynamic List Sorting

By , 14 Feb 2006
 

Introduction

This article spawned from a post on the Danish site DotNetForum.dk. The poster asked how you would go about sorting a list of, say, person objects, in a dynamic fashion like SQL. At that point, somebody mentioned the IComparer and IComparer<> interfaces and that you probably could make some sort of generic comparer using those interfaces. And a link to an article by Peter Provost named “Generic Property Comparer Class” was also provided. The article explains how to make a generic comparer using IComparer in .NET 1.1. Peter’s solution is okay considering .NET 1.1 but it’s rather bad in a .NET 2.0 context for several reasons:

  1. It only sorts one property at a time.
  2. It only sorts in ascending order.
  3. It isn’t strongly typed.
  4. It is very slow!

Note: Point 3 and 4 are just an artifact of the fact that .NET 1.1 is lacking both generics and dynamic methods.

So this made me decide that I would try to come up with a better solution utilizing the new generics in .NET 2.0 plus some other very cool features of the framework.

A key point to keep in mind when starting a project like this is performance. I mean, nobody wants to wait 5 seconds while a list gets sorted. Another key point in this project was that it should be dynamic and should, if possible, enable the user to enter a string with a SQL “Order By” like syntax, e.g., “FirstName, Age DESC, LastName”.

The Solution

The first solution I jammed out was a comparer which only used generics and was able to sort ascending as well as descending on any number of properties. So this solution met most of the important requirements. However, even though I made this before I saw Peter's 1.1 solution, they were in fact very similar except mine used generics for specifying the type of objects whereas in Peter’s solution, you pass in the item type as a parameter. Unfortunately, this solution also had the performance problem. After a few performance tests, it was clear to me that this solution was not an option.

The problem is that you can’t really fulfill the requirements without dynamically generating some amount of specific code. At least, that was the conclusion I made after a while. Fortunately, .NET 2.0 has a new feature called “dynamic methods”.

A “dynamic method” is a method you create in-memory during runtime. I won’t dive deeper into this subject but I recommend that you check it out. Basically, what I do is I compile a strongly typed version of the “Compare” method in-memory and call it using a delegate. As you will see in the next section, this is a very powerful way of getting a strongly typed method while keeping it completely dynamic and very fast.

Performance Comparison

I’ve done a few performance tests of the most commonly used sorting methods along with Peter Provost’s solution. The results are listed below:

Diagram of performance test results

Fig. 1: Sorting times for 10 to 100000 items.

As you see in fig. 1, the dynamic comparer solution is a good way of sorting your custom entity lists. The dynamic comparer is sharing the first place with the hard-coded comparer. The hard-coded comparer seems to be starting out slowly but that is just a measuring error. This clearly shows that from the runtime's perspective, the dynamic method used to sort is pretty much like the hard-coded one except that we call it through a delegate.

On the second place, we have both the weakly and the strongly typed datasets. Both of them are following the same line which is a little steeper than the other methods. That means that they are getting slower at a faster rate compared to the other methods. Some might think that the strongly typed dataset should be sorting faster than the weakly typed dataset but think again… The strongly typed dataset is just an overloaded version of the weakly typed dataset, so in fact, it is using the same sorting methods as the weakly typed version. It is also worth mentioning that populating a dataset with 10000 items or more is much slower than populating the regular lists.

Finally, on the third place, we have the generic class comparer. As you can see, it is much slower than the other methods of sorting. Not because it is badly designed or coded, but because .NET 1.1 supports neither generics nor dynamic methods.

Usage

So how do you use this comparer? Well, it’s as easy as 1-2-3:

//Example:
string orderBy = "FirstName, LastName DESC"; // 1
DynamicComparer<Person> comparer = new DynamicComparer<Person>(orderBy); // 2
persons.Sort(comparer.Compare); // 3
  1. Declare a sort string or a SortProperty array. Note: This could be created and maintained by a custom editgrid.
  2. Declare an instance of the DynamicComparer class using the generic constructor.
  3. Call the sort method on the List<Person>.

That’s it!

So what’s going on behind the scenes? Let’s begin at line 2 of the previous example. At the time of instantiation of the DynamicComparer, the class will first parse the input string into a SortProperty array. These SortProperties will then be validated against the type we specified when instantiating the DynamicComparer, in this case, “Person”. This validation checks if the Person class is public, if it has any public properties named “FirstName” and “LastName”, if these properties are readable, and finally, if these properties are of a type that implements the IComparable interface. If any of these validations fail, the class will throw an exception. If the validation succeeds, the class will then generate a dynamic method using the specified SortPropertyies and instantiate an internal delegate pointing to the method. The “Compare” method on the DynamicComparer will then in turn be able to invoke this delegate when called. This means that the comparer is ready for use.

Note: If you want to change the sorting of an instance of the DynamicComparer, you can call the “Initialize” method on the instance and pass in a new ORDER BY string or a SortProperty array.

At the next step, we call the “Sort” method on the “persons” list passing in a reference to the comparer.Compare method.

Conclusion

The dynamic comparer seems to be a very efficient way of getting a flexible comparer while keeping it fast, and I’ve yet to see a better or even just a different solution that has the same flexibility and performance. Still, a couple of things could be improved in the comparer but mostly in the instantiation process. I leave it to you to come up with new suggestions and ideas to improve the comparer. I'm looking forward for your feedback!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Johannes Hansen
Web Developer Jubii A/S
Denmark Denmark
Member
Johannes Hansen was born in denmark during a thunderstorm in mid-june 1980.
 
He grew up stealing time on his father's C64 and finally got his own computer in 1995.
 
He currently work as a developer/ at the danish company Jubii A/S where he spends his time developing spam filters, portals and oher user-driven solutions using C#.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralWorth Readingmemberbilal7869917 Mar '13 - 22:06 
Nice one. For understanding Lists visit:
http://codesmesh.com/dynamic-array-using-list-csharp/[^]
GeneralThanksmemberJon Cunningham26 Nov '11 - 4:43 
This saved me alot of time... I needed a dynamic sort on some lists and this code fit the bill.
 

Thanks again,
Jon Cunningham
Jon Cunningham

GeneralMy vote of 5memberSBaghestani27 Jun '10 - 1:38 
thank yoy for your code
GeneralIt Works in Vb.net as well but...memberOslec2 Dec '08 - 16:22 
I add it on my project reference and create a function
 
Public Function DynamicIListComparer(ByVal orderBy As String, ByVal pages As List(Of PageIListCollection)) As List(Of PageIListCollection)

Dim comparer As New DynamicComparer(Of PageIListCollection)(orderBy)
pages.Sort(comparer)
Return pages
 
End Function

 
It works well!..but if i changed the code to pages.Sort(comparer.Compare)
 
i got an error..
 
Error 2 Argument not specified for parameter 'x' of 'Public Function Compare(x As T, y As T) As Integer'.
 
any suggestions.. Confused | :confused:
 
By the way thanks Johannes Hansen Big Grin | :-D
AnswerRe: It Works in Vb.net as well but...memberJohannes Hansen2 Dec '08 - 20:23 
Hi Oslec
 
First of all, thank you for getting involved. Wink | ;)
 
Secondly, I'm not sure why you are experiencing that particular problem, but I do know that my original code had some bugs in it and that the implementation Marc Brooks did is much more robust. So I recommend you try his implementation out and see if that works better (which I'm sure it will). You can grab his implementation at the Dynamic Reflection Library's project site on Codeplex[^]. If you have any comments/bug reports/suggestions to Marc's implementation please post them on the Codeplex site, thanks. Smile | :)
 
Kind regards,
Johannes Hansen
frontAvenue A/S

Questionnull string problemmemberColinBashBash29 Sep '08 - 10:03 
ok. i just noticed that if i have null string intermixed in my List's it errors out. any help would be wonderful. I do love this solution that you've put out here. I hope you'll get a chance to look at this soon.
 
i tried looking into the "#region Generate IL for dynamic method," but i got lost too quickly to get anywhere with it. (at the bottom i have a separate question on templates)
 
public class Project{
  string _entryCode;
  public string EntryCode{
    get{return _entryCode;}
    set{_entryCode=value;}
  }
  public Project() {}
  public Project(string ec) {_entryCode = ec;}
}
 

//More test code to show error
List<project> projects = new List<project>();
 
projects.Add(new Project());
projects.Add(new Project());
projects.Add(new Project("asf"));
projects.Add(new Project("veev"));
projects.Add(new Project());
projects.Add(new Project("asdfe"));
projects.Add(new Project());
projects.Add(new Project());
 
projects.Sort(new DynamicComparer<project>("EntryCode"));
 
gridview1.DataSource = projects;
gridview1.DataBind();
 
</project></project></project>
 

=========================================
===========Error Message=================
=========================================
Object reference not set to an instance of an object.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
 
Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.
 
Source Error:
 
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.
 
Stack Trace:
 

[NullReferenceException: Object reference not set to an instance of an object.]
DynamicComparison(Project , Project ) +12
DynamicComparer.DynamicComparer`1.Compare(T x, T y) in C:\Documents and Settings\colin.cole\My Documents\Visual Studio 2005\Projects\comparer\DynamicComparer\DynamicComparer.cs:65
System.Collections.Generic.ArraySortHelper`1.SwapIfGreaterWithItems(T[] keys, TValue[] values, IComparer`1 comparer, Int32 a, Int32 b) +125
 
[InvalidOperationException: Failed to compare two elements in the array.]
System.Collections.Generic.ArraySortHelper`1.SwapIfGreaterWithItems(T[] keys, TValue[] values, IComparer`1 comparer, Int32 a, Int32 b) +433
System.Collections.Generic.ArraySortHelper`1.QuickSort(T[] keys, TValue[] values, Int32 left, Int32 right, IComparer`1 comparer) +107
System.Collections.Generic.ArraySortHelper`1.Sort(T[] keys, TValue[] values, Int32 index, Int32 length, IComparer`1 comparer) +129
System.Collections.Generic.ArraySortHelper`1.Sort(T[] items, Int32 index, Int32 length, IComparer`1 comparer) +125
System.Array.Sort(T[] array, Int32 index, Int32 length, IComparer`1 comparer) +193
System.Collections.Generic.List`1.Sort(Int32 index, Int32 count, IComparer`1 comparer) +93
 

==================================
==================================
==================================
 
is it possible to write the DynamicMethod piece in a similar way as to a generic template? (see below.) I'm assuming you can't, since it looks like it's generating assembly level code. Or is the generic implementation method just slower? I'm just curious, since i know so little about this. I am in no way trying to be critical.
 
(note the GenericTemplateImplementation can be found here, written by yet someone else who's smarter than me: http://couldbedone.blogspot.com/2007/07/implementing-itemplate-as-anonymous.html[^].
 
 
    public delegate void InstantiateTemplateDelegate(Control container);
 
    public class GenericTemplateImplementation : ITemplate {
        private InstantiateTemplateDelegate m_instantiateTemplate;
 
        public void InstantiateIn(Control container) {
            m_instantiateTemplate(container);
        }
 
        public GenericTemplateImplementation(InstantiateTemplateDelegate instantiateTemplate) {
            m_instantiateTemplate = instantiateTemplate;
        }
    }
 
            rptCategory.ItemTemplate = new GenericTemplateImplementation(
                delegate(Control cont) {
                    RepeaterItem Container = (RepeaterItem)cont;
 
                    if (Container.ItemIndex != 0)
                        Container.Controls.Add(new LiteralControl(" > "));
                    LinkButton b = new LinkButton();
                    b.ID = "lnk";
                    b.CommandName = "lnk";
                    Container.Controls.Add(b);
                });
 

AnswerRe: null string problem [modified]memberJohannes Hansen2 Oct '08 - 17:42 
Hi Colin, sorry for the late reply.
 
I'm not planning to make any fixes or changes to the current implementation of the DynamicComparer. But don't be sad, go take a look at Marc Brook's implementation of the DynamicComparer at CodePlex[^]. I'm pretty sure his implementation fixes most of the bugs in my initial prototype/example implementation.
 
As to your second question, I'm not really sure I understand it, but I'll try to answer it anyways. Wink | ;) Early on in the project I did experiment with a generic comparer but it turned out to be dreadfully slow. This is mainly because you then have to use reflection to lookup field and property types at runtime as well as casting the values before comparison. So no-go for a generic only implementation.
 
If you wanted to implement a simpler DynamicComparer it could probably be done in .NET 3.5 using LINQ expression trees. However, they might be a little slower than Marc's implementation since expression trees probably has to be more generalized, and thus has to do more checks at runtime (I haven't tried it though so I might be completely wrong). Also, Marc's implementation is extremely streamlined and is faster than compiled code in some (maybe even most) cases.
 
modified on Thursday, October 2, 2008 11:57 PM

GeneralRe: null string problemmemberColinBashBash3 Oct '08 - 3:33 
No problem. Thanks!
Questioncan this order propertys from composite classes?membercottsak10 Apr '08 - 16:49 
can this order propertys from composite classes?
like this class can:
 

/*------------------------------------------------------------------------
      This class is adapted from:
* http://www.rosscode.com/blog/index.php?title=generic_multiple_sorting_for_typed_colle&more=1&c=1&tb=1&pb=1
* and http://www.codeplex.com/NSFxSamples/SourceControl/FileView.aspx?itemId=212083&changeSetId=16449
      (thanx Joel)
     
      Created: 2008.04.10
      Author: Matthew Kocaj
------------------------------------------------------------------------*/
 
using System;
using System.Collections.Generic;
using System.Reflection;
 

public enum OrderDirection
{
      Ascending,
      Descending
}
 
/// <summary>
/// used to sort generic collections.
/// will accept n number of columns where
/// those columns are in any n number
/// of composite classes for each item
/// </summary>
/// <typeparam name="T"></typeparam>
public class EntityComparer<T> : IComparer<T>
{
      private List<OrderItem> _orderItems = new List<OrderItem>();
 

      #region Constructors
 
      public EntityComparer() { }
 
      public EntityComparer(List<OrderItem> orderItems)
      {
            this._orderItems = orderItems;
      }
 
      public EntityComparer(string sortColumn, OrderDirection orderDirection)
      {
            this._orderItems.Add(new OrderItem(sortColumn, orderDirection));
      }
 
      /// <summary>
      /// used for sort expressions in the form "Column1 DESC, Column2 ASC, ..."
      /// </summary>
      /// <param name="sortExpression"></param>
      public EntityComparer(string sortExpression)
      {
            if (sortExpression != String.Empty)
            {
                  string[] fields = sortExpression.Split(',');
                  foreach (string field in fields)
                  {
                        string[] parts = field.Trim().Split(' ');
                        if (parts[1] == "ASC")
                              this._orderItems.Add(new OrderItem(parts[0], OrderDirection.Ascending));
                        if (parts[1] == "DESC")
                              this._orderItems.Add(new OrderItem(parts[0], OrderDirection.Descending));
                  }
            }
      }
 
      #endregion
 

      public List<OrderItem> OrderItems
      {
            get { return this._orderItems; }
            set { this._orderItems = value; }
      }
 

      #region IComparer<T> Members
 
      public int Compare(T x, T y)
      {
            if (this._orderItems.Count == 0)
                  return 0;
            else
                  return this.CheckSort(0, x, y);
      }
 
      #endregion
 
      /// <summary>
      /// Recursive function to do sorting for multiple columns
      /// </summary>
      /// <param name="sortIndex">The current index of the column we're sorting at</param>
      /// <param name="x"></param>
      /// <param name="y"></param>
      /// <returns></returns>
      private int CheckSort(int sortIndex, T x, T y)
      {
            int returnValue = 0;
 
            if (this._orderItems.Count - 1 >= sortIndex)
            {
                  if (x.GetType().ToString() == y.GetType().ToString())
                  {
                        string[] propertyList = this._orderItems[sortIndex].SortColumn.Split('.');
 
                        object currentObject1 = x;
                        object currentObject2 = y;
 
                        for (int i = 0; i < propertyList.Length; i++)
                        {
                              PropertyInfo property = currentObject1.GetType().GetProperty(propertyList.GetValue(i).ToString());
 
                              if (property != null && property.CanRead)
                              {
                                    currentObject1 = this.GetPropertyValue(property, currentObject1);
                                    currentObject2 = this.GetPropertyValue(property, currentObject2);
                              }
                        }
 
                        if (currentObject1 != null && currentObject2 != null)
                        {
                              //   Assume all property types used are
                              //   Comparable
                              IComparable value1 = (IComparable)currentObject1;
                              IComparable value2 = (IComparable)currentObject2;
 
                              returnValue = value1.CompareTo(value2);
                        }
 
                        // apply Descending differences
                        if (this._orderItems[sortIndex].OrderDirection == OrderDirection.Descending)
                              returnValue = returnValue * -1;
                  }
 
                  // call again for multiple columns
                  if (returnValue == 0)
                        returnValue = this.CheckSort(sortIndex + 1, x, y);
            }
 
            return returnValue;
      }
 
      /// <summary>
      /// Get Property value
      /// </summary>
      /// <param name="property">Property</param>
      /// <param name="entity">Entity</param>
      /// <returns>Value</returns>
      private object GetPropertyValue(PropertyInfo property, object entity)
      {
            return property.GetValue(entity, null);
      }
 

      public class OrderItem
      {
            private string _sortColumn = String.Empty;
            private OrderDirection _orderDirection = 0;
 

            public OrderItem(string sortColumn, OrderDirection orderDirection)
            {
                  this._sortColumn = sortColumn;
                  this._orderDirection = orderDirection;
            }
 

            public string SortColumn
            {
                  get { return this._sortColumn; }
            }
 
            public OrderDirection OrderDirection
            {
                  get { return this._orderDirection; }
            }
      }
}
GeneralRe: can this order propertys from composite classes?memberJohannes Hansen10 Apr '08 - 21:17 
Hi,
 
Yes, the comparer that I've suggested can order any type of object, please take a look at Marc Brooks implementation for an improved version of the DynamicComparer.
 
The problem with the EntityComparer you've sent is the it will probably perform very poorly because it's using reflection, type casting, recursion and other nasty stuff in the CheckSort method. Actually I think this will perform worse than the "GenericClassComparer" from the chart in my article.
 
Kind regards,
Johannes Hansen
frontAvenue A/S

GeneralGet an error when adding many sorting items ...memberzhucalvin14 Jan '08 - 11:12 
Hi everyone,
 
I have implemented the coding on my application. Everything is good except I got an error when adding 6 more sorting items:
 
====================================================================
Illegal one-byte branch at position: 4. Requested branch was: 207.
 
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
 
Exception Details: System.NotSupportedException: Illegal one-byte branch at position: 4. Requested branch was: 207.
 
Source Error:
 

Line 51: CheckSortProperties(sortProperties);
Line 52: method = CreateDynamicCompareMethod(sortProperties);
Line 53: comparer = (CompareMethodInvoker)method.CreateDelegate(typeof(CompareMethodInvoker));
Line 54: }
Line 55:
=====================================================================
 
I didn't get the updated version yet. What I implemented is the verion attached on this article. Can anybody send me a email with the updated version, please? zhucalvin@yahoo.com
I do appreciate it if anybody can help me out.
 
Thanks.
 
- Calvin
GeneralRe: Get an error when adding many sorting items ...memberMarc Brooks14 Jan '08 - 15:00 
That's due to the emitted branch instructions being 'short' or relative branches. You can change the emitted opcodes to be regular branches instead of short branches. The other option is to grab my current version from CodePlex Dynamic Reflection Librarywhich avoids this issue.
 
http://musingmarc.blogspot.com

GeneralThanks!membervtsantos2 Nov '07 - 9:21 
Thanks!!
 
Great article! Help me a lot!
 
Big Grin | :-D
 

 
Valeria
GeneralSorting on properties of propertiesmemberngruson17 Aug '07 - 4:23 
Hi Johannes,
 
First of all, good work you did on the DynamicComparer. It's really fast indeed.
 
I made some changes to it to support sorting on properties of properties.
Let's say I have an Customer class which has a CurrentAddress property of the type Address.
The Address class has properties like City and Street. What I wanted to do, is sorting customers on their city and street. As far as I could see, this functionality is not supported by the comparer.
 
After doing some research on dynamic methods, I modified the DynamicComparer to meet my needs described above. Now I call the DynamicComparer like this:
 
DynamicComparer<Customer> comparer = new DynamicComparer<Customer>("Address.City, Address.Street");
List<Customer> list = (List<Customer> )dao.GetAll();
list.Sort(comparer.Compare);

 
Maybe you can take a look and test it.
 
Greets,
Nils Gruson
 
Here is the code I changed:
 
protected DynamicMethod CreateDynamicCompareMethod(SortProperty[] sortProperties)
{
DynamicMethod dm = new DynamicMethod("DynamicComparison", typeof(int), new Type[] { typeof(T), typeof(T) }, typeof(DynamicComparer));
 
#region Generate IL for dynamic method
ILGenerator ilGen = dm.GetILGenerator();
 
Label lbl = ilGen.DefineLabel(); // Declare and define a label that we can jump to.
ilGen.DeclareLocal(typeof(int)); // Declare a local variable for storing result.
Dictionary localVariables = new Dictionary();
 
ilGen.Emit(OpCodes.Ldc_I4_0); // Push 0 onto the eval stack.
ilGen.Emit(OpCodes.Stloc_0); // Store the eval stack item in the local variable @ position 0.

Type propertyType;
PropertyInfo propertyInfo, childPropertyInfo = null;

foreach (SortProperty property in sortProperties) // For each of the properties we want to check inject the following il.
{
string[] properties = property.Name.Split('.');
 
ilGen.Emit(OpCodes.Ldloc_0); // Load local variable at position 0.
ilGen.Emit(OpCodes.Brtrue_S, lbl); // Is the local variable in the evaluation stack equal to 0. If not jump to the label we just defined.
ilGen.Emit(OpCodes.Ldarg_0); // Load argument at position 0.
 
propertyType = typeof(T);

foreach (string propertyName in properties)
{
propertyInfo = propertyType.GetProperty(propertyName);
ilGen.EmitCall(OpCodes.Callvirt, propertyInfo.GetGetMethod(), null);
propertyType = propertyType.GetProperty(propertyName).PropertyType;
childPropertyInfo = propertyInfo;
}
 
if (childPropertyInfo.PropertyType.IsValueType) // If the type is a valuetype then we need to inject the following IL.
{
if (!localVariables.ContainsKey(childPropertyInfo.PropertyType)) // Do we have a local variable for this type? If not, add one.
localVariables.Add(childPropertyInfo.PropertyType, ilGen.DeclareLocal(childPropertyInfo.PropertyType)); // Adds a local variable of type x.
 
int localIndex = localVariables[childPropertyInfo.PropertyType].LocalIndex; // This local variable is for handling value types of type x.
 
ilGen.Emit(OpCodes.Stloc, localIndex); // Store the value in the local var at position x.
ilGen.Emit(OpCodes.Ldloca_S, localIndex); // Load the address of the value into the stack.
}
 
propertyType = typeof(T);

ilGen.Emit(OpCodes.Ldarg_1); // Load argument at position 0.
foreach (string propertyName in properties)
{
propertyInfo = propertyType.GetProperty(propertyName);
ilGen.EmitCall(OpCodes.Callvirt, propertyInfo.GetGetMethod(), null);
propertyType = propertyType.GetProperty(propertyName).PropertyType;
}
 
// Compare the top 2 items in the evaluation stack and push the return value into the eval stack.
ilGen.EmitCall(OpCodes.Callvirt, childPropertyInfo.PropertyType.GetMethod("CompareTo", new Type[] { childPropertyInfo.PropertyType }), null);
 
if (property.Descending) // If the sort should be descending we need to flip the result of the comparison.
ilGen.Emit(OpCodes.Neg); // Negates the item in the eval stack.
 
ilGen.Emit(OpCodes.Stloc_0); // Store the result in the local variable.
}
 
ilGen.MarkLabel(lbl); // This is the spot where the label we created earlier should be added.
ilGen.Emit(OpCodes.Ldloc_0); // Load the local var into the eval stack.
ilGen.Emit(OpCodes.Ret); // Return the value.
#endregion
 
return dm;
}
 
protected void CheckSortProperties(SortProperty[] sortProperties)
{
if (sortProperties == null)
sortProperties = new SortProperty[0];
 
Type instanceType = typeof(T);
if (!instanceType.IsPublic)
throw new ArgumentException(string.Format("Type \"{0}\" is not public.", typeof(T).FullName));
 
foreach (SortProperty sProp in sortProperties)
{
string[] properties = sProp.Name.Split('.');
PropertyInfo pInfo = null;
 
Type propertyType = instanceType;
foreach (string propertyName in properties)
{
pInfo = propertyType.GetProperty(propertyName);
propertyType = propertyType.GetProperty(propertyName).PropertyType;
}

if (pInfo == null)
throw new ArgumentException(string.Format("No public property named \"{0}\" was found.", sProp.Name));
 
if (!pInfo.CanRead)
throw new ArgumentException(string.Format("The property \"{0}\" is write-only.", sProp.Name));
 
if (!typeof(IComparable).IsAssignableFrom(pInfo.PropertyType))
throw new ArgumentException(string.Format("The type \"{1}\" of the property \"{0}\" does not implement IComparable.", sProp.Name, pInfo.PropertyType.FullName));
}
}

GeneralRe: Sorting on properties of propertiesmemberJohannes Hansen17 Aug '07 - 19:03 
Hi Nils
 
Good work on the upgrade... As far as I can tell it should work just as well as the ordinary DynamicComparer. Unfortunately I don't have the time to test it any more than this at the moment. However, you should take a look at the implementation Marc did. I'm not sure if it has child property capabilities but his implementation is far more robust than mine is so it would be a better base for your upgrade. Also, If you're planning on switching to .NET 3.5 you should take a good look at expression trees which could probably help making the DynamicComparer much simpler.
 
Hope this helps.
 
Kind regards,
Johannes Hansen
frontAvenue A/S

QuestionCompare enum typememberqsupastar18 Jun '07 - 10:25 
It's able to compare and sort System Types but can it compare enum properties in a class?
 
Example:
 
public static class SampleEnumClassName
{
public enum SampleEnum
{
EnumV1,
EnumV2
}
}
 
public class SampleClass
{
private SampleEnumClassName.SampleEnum pTestEnum;
public SampleEnumClassName.SampleEnum _TestEnum
{
get{return pTestEnum;}
set{pTestEnum = value;}
}
 
public SampleClass(){}
}

AnswerRe: Compare enum typememberJohannes Hansen18 Jun '07 - 16:00 
I'm not sure if my implementation of the DynamicComparer works with enums but I'm pretty sure that the implementation that Marc Brooks made do sort enums. You can find a link to his implementation in one of the earlier post in this comments section by him. Please try his implementation out or alternatively have a look at Linq and expression trees in .net 3.5 which can provide similar functionality and that will definately work with enums.
 
Hope this helps...
 
Kind regards,
Johannes Hansen
frontAvenue A/S

QuestionFiltering ?memberAcidBUG13 Apr '07 - 15:30 
Hi Guys, has anyone made a filtering class using this method. I would be very interested in seeing it.
 
-Thanks
GeneralDynamicComparer step aside for the mighty Linq project. All hail the Linq!memberJohannes Hansen9 Apr '07 - 19:53 
As much of the functionality of the DynamicComparer will be overlapped by the functionality of te various Linq implementations in .NET 3.5. Therefore I won't develop/support this project any further as the Linq project will get a go-live license in june. Thanks for all the support and help which made this project a very rich learning experience.
 

 
Kind regards,
Johannes Hansen
frontAvenue A/S

GeneralRe: DynamicComparer step aside for the mighty Linq project. All hail the Linq!memberSimone Busoli25 Apr '07 - 0:32 
Thank you very much for your contribution then. We'll see if LINQ is going to be as performant as your implementation!
 
Simone Busoli

GeneralNull CheckmemberTerrance A. Snyder19 Mar '07 - 7:28 
The null check seems to be invalid when working with custom objects, the fix seems to be using OpCodes.Brfalse_S instead of OpCodes.Brtrue_S.
 
// CreateDynamicCompareMethod(...)
-> ilGen.Emit(OpCodes.Brfalse_S, lbl); // Is the local variable in the evaluation stack equal to 0. If not jump to the label we just defined (fixed by replacing Bftrue_S).
Questionhow to use with a GridView?memberbraditude7 Feb '07 - 11:11 
I must be dense, but I am unable to determine how to use this with a GridView that is populated with a objectDataSource
 
I know I need to do the work on the Grid_Sorting event, but my attempt
 
protected void DGrid_Sorting(object sender, GridViewSortEventArgs e)
{
string orderBy = e.SortExpression + " " + e.SortDirection;
DynamicComparer comparer = new DynamicComparer(orderBy);
List<> Ds = objDSDocuments.Select();
Ds.Sort(new DynamicComparer(e.SortExpression, e.SortDirection));
DGrid.DataSource = Ds;
DGrid.DataBind();
}
 
generates errors.
 
Any thoughts?

GeneralNullReferenceException when sorting on a stringmemberTroye Stonich30 Dec '06 - 13:46 
Hi Johannes,
I've downloaded your excellent project and am implementing it in my application.
 
I've got my generic list List where EventDTO has a string member called "Location". The "Location" column in my SQL database is an optional varchar. I populate this list using a SqlDataReader, knowing that if "Location" is null in the DB, the member will also come back null. So, I try to sort on this column and I get a NullReferenceException in the "Compare" method (line 65 in the original DynamicComparer class).
 
Also, did you ever update this class to handle Nullables? I saw on another thread that you had talked about it, but I couldn't download your updated code.
 
Many Thanks!
Troye Stonich
GeneralRe: NullReferenceException when sorting on a stringmemberJohannes Hansen1 Jan '07 - 0:46 
Hi Troye
 
Thanks for your bug-report... I recommend that you take a look at Marc Brook's more robust implementation of my technique into his very cool utility library called "Utilities" which you can download here[^]. If you still get the error after switching to his implementation please submit an bug-report to Marc.
 
The update to the library using nullables has turned out to be a dud. Early performance tests showed the code to be awfully slow. So even though the code were MUCH simpler I didn't think it should be released.
 
I hope this helps...
 
Kind regards and happy new year!
Johannes Hansen
frontAvenue A/S

QuestionUnique filtermembermac24nzmac24nz18 Oct '06 - 4:22 
Hi - I realise this is a bit out of place - but I can't figure out where to find this info on the net.
 
I've been playing with ADO.NET - Datasets, etc for a couple of months now - and I can't seem to find one basic part of SQL - the unique keyword. I know how to make a column unique - but not a search query.
 
e.g. How would i perform the following query?
 
SELECT UNIQUE col1, col2 from table
 
where the table might include many columns of which col1, col2 are not primary keys - or are not the only primary keys.....
 
I'm sure this is really easy - but I've wasted hours without success. Can anyone help me out?
 
Thanks
 
Martin

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 14 Feb 2006
Article Copyright 2005 by Johannes Hansen
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid