随着实体的概念的流行,DataTable正在逐渐的淡出我们的视野,但是,有些时候,DataTable很容易做的一件事情,却让我们不得不头痛起来——按指定列排序
  如果数据是来源于一个sql语句,那么,我们可以把这个问题转嫁给sql,让DB来解决这个问题。
  但是,如果数据来源于某些不支持排序(例如webservice并且没有提供排序功能),或者其他复杂情况,这样就不得不依赖于.net的集合排序。可是,如果一个系统有10个这样的类,平均每个类有10个属性可能被选择,那么就意味着有100个排序方法需要被写(c#3.0的Lambda表达式当然可以为我们省下很多力气)。如果,有一个方法直接输入属性的名称,就返回一个按照对应属性的比较器,岂不是更容易?
  基于这个想法,再加上效率的考量,用Emit写了简单的单一属性的使用默认比较器的排序:

 Code
Code
 1 public static class Extensions
    public static class Extensions
 2
 
     {
{
 3 public static void SortByProperty<T>(this T[] array, string propName)
        public static void SortByProperty<T>(this T[] array, string propName)
 4
 
         {
{
 5 Array.Sort(array, Inner<T>.GetComparison(propName));
            Array.Sort(array, Inner<T>.GetComparison(propName));
 6 }
        }
 7
 8 public static void SortByProperty<T>(this List<T> list, string propName)
        public static void SortByProperty<T>(this List<T> list, string propName)
 9
 
         {
{
10 list.Sort(Inner<T>.GetComparison(propName));
            list.Sort(Inner<T>.GetComparison(propName));
11 }
        }
12
13 private static class Inner<T>
        private static class Inner<T>
14
 
         {
{
15 private static Dictionary<string, Comparison<T>> m_cache =
            private static Dictionary<string, Comparison<T>> m_cache =
16 new Dictionary<string, Comparison<T>>();
                new Dictionary<string, Comparison<T>>();
17
18 internal static Comparison<T> GetComparison(string propName)
            internal static Comparison<T> GetComparison(string propName)
19
 
             {
{
20 Comparison<T> comparison;
                Comparison<T> comparison;
21 if (!m_cache.TryGetValue(propName, out comparison))
                if (!m_cache.TryGetValue(propName, out comparison))
22 m_cache[propName] = comparison = GetComparisonNoCache(propName);
                    m_cache[propName] = comparison = GetComparisonNoCache(propName);
23 return comparison;
                return comparison;
24 }
            }
25
26 private static Comparison<T> GetComparisonNoCache(string propName)
            private static Comparison<T> GetComparisonNoCache(string propName)
27
 
             {
{
28 bool isValueType = typeof(T).IsValueType;
                bool isValueType = typeof(T).IsValueType;
29 var prop = typeof(T).GetProperty(propName);
                var prop = typeof(T).GetProperty(propName);
30 var comparerType = typeof(Comparer<>).MakeGenericType(prop.PropertyType);
                var comparerType = typeof(Comparer<>).MakeGenericType(prop.PropertyType);
31 DynamicMethod dm = new DynamicMethod(string.Empty, typeof(int),
                DynamicMethod dm = new DynamicMethod(string.Empty, typeof(int),
32
 new Type[]
                    new Type[]  { typeof(T), typeof(T) }, typeof(T));
{ typeof(T), typeof(T) }, typeof(T));
33 var il = dm.GetILGenerator();
                var il = dm.GetILGenerator();
34 il.Emit(OpCodes.Call, comparerType.GetProperty("Default").GetGetMethod());
                il.Emit(OpCodes.Call, comparerType.GetProperty("Default").GetGetMethod());
35 if (isValueType)
                if (isValueType)
36
 
                 {
{
37 il.Emit(OpCodes.Ldarga_S, 0);
                    il.Emit(OpCodes.Ldarga_S, 0);
38 il.Emit(OpCodes.Call, prop.GetGetMethod());
                    il.Emit(OpCodes.Call, prop.GetGetMethod());
39 il.Emit(OpCodes.Ldarga_S, 1);
                    il.Emit(OpCodes.Ldarga_S, 1);
40 il.Emit(OpCodes.Call, prop.GetGetMethod());
                    il.Emit(OpCodes.Call, prop.GetGetMethod());
41 }
                }
42 else
                else
43
 
                 {
{
44 il.Emit(OpCodes.Ldarg_0);
                    il.Emit(OpCodes.Ldarg_0);
45 il.Emit(OpCodes.Callvirt, prop.GetGetMethod());
                    il.Emit(OpCodes.Callvirt, prop.GetGetMethod());
46 il.Emit(OpCodes.Ldarg_1);
                    il.Emit(OpCodes.Ldarg_1);
47 il.Emit(OpCodes.Callvirt, prop.GetGetMethod());
                    il.Emit(OpCodes.Callvirt, prop.GetGetMethod());
48 }
                }
49 il.Emit(OpCodes.Callvirt, comparerType.GetMethod("Compare"));
                il.Emit(OpCodes.Callvirt, comparerType.GetMethod("Compare"));
50 il.Emit(OpCodes.Ret);
                il.Emit(OpCodes.Ret);
51 return (Comparison<T>)dm.CreateDelegate(typeof(Comparison<T>));
                return (Comparison<T>)dm.CreateDelegate(typeof(Comparison<T>));
52 }
            }
53 }
        }
54 }
    }
55
   因为,考虑到经常需要排序可空类型(例如:int?),所以,使用Comparer<T>.Default的Compare方法,当然性能上略有损失,在属性是int的情况下,与直接用Lambda相比,性能大概是5:6,也就是说在数组相对比较大时,如果用Lambda需要5s,那么用这个方法可能就需要6s。功能上来说,由于无法传入比较器,排序功能上比较弱,只能应付用属性的默认比较器的排序的情况。