Dynamic chaining of List orderby
本问题已经有最佳答案,请猛点这里访问。
我正在编写一个列表排序扩展方法。
我的输入是列表和带有属性名称和排序方向的字符串。
这个字符串可以有多个属性,如下所示:
"命名ASC,日期DESC"等
我已经实现了字符串解析并使用了反射来从字符串中获取属性本身,但我现在所困扰的是如何动态链接orderby方法。
就像是:
有没有办法动态构建它?
使用反射从字符串属性名称获取到PropertyInfo。然后,您可以使用PropertyInfo构建表达式树,以动态构建所有orderbys。获得表达式树后,将其编译为委托,(比如Func,IEnumerable>)将_list参数传递给此委托,它将为您提供另一个可枚举的有序结果。
要获取Enumerable上泛型方法的反射信息,请查看此帖子的答案:
在不使用GetMethods的情况下获取泛型方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | public static class Helper { public static IEnumerable< T > BuildOrderBys< T >( this IEnumerable< T > source, params SortDescription[] properties) { if (properties == null || properties.Length == 0) return source; var typeOfT = typeof (T); Type t = typeOfT; IOrderedEnumerable< T > result = null; var thenBy = false; foreach (var item in properties .Select(prop => new {PropertyInfo = t.GetProperty(prop.PropertyName), prop.Direction})) { var oExpr = Expression.Parameter(typeOfT,"o"); var propertyInfo = item.PropertyInfo; var propertyType = propertyInfo.PropertyType; var isAscending = item.Direction == ListSortDirection.Ascending; if (thenBy) { var prevExpr = Expression.Parameter(typeof (IOrderedEnumerable< T >),"prevExpr"); var expr1 = Expression.Lambda<Func<IOrderedEnumerable< T >, IOrderedEnumerable< T >>>( Expression.Call( (isAscending ? thenByMethod : thenByDescendingMethod).MakeGenericMethod(typeOfT, propertyType), prevExpr, Expression.Lambda( typeof (Func<,>).MakeGenericType(typeOfT, propertyType), Expression.MakeMemberAccess(oExpr, propertyInfo), oExpr) ), prevExpr) .Compile(); result = expr1(result); } else { var prevExpr = Expression.Parameter(typeof (IEnumerable< T >),"prevExpr"); var expr1 = Expression.Lambda<Func<IEnumerable< T >, IOrderedEnumerable< T >>>( Expression.Call( (isAscending ? orderByMethod : orderByDescendingMethod).MakeGenericMethod(typeOfT, propertyType), prevExpr, Expression.Lambda( typeof (Func<,>).MakeGenericType(typeOfT, propertyType), Expression.MakeMemberAccess(oExpr, propertyInfo), oExpr) ), prevExpr) .Compile(); result = expr1(source); thenBy = true; } } return result; } private static MethodInfo orderByMethod = MethodOf(() => Enumerable.OrderBy(default(IEnumerable<object>), default(Func<object, object>))) .GetGenericMethodDefinition(); private static MethodInfo orderByDescendingMethod = MethodOf(() => Enumerable.OrderByDescending(default(IEnumerable<object>), default(Func<object, object>))) .GetGenericMethodDefinition(); private static MethodInfo thenByMethod = MethodOf(() => Enumerable.ThenBy(default(IOrderedEnumerable<object>), default(Func<object, object>))) .GetGenericMethodDefinition(); private static MethodInfo thenByDescendingMethod = MethodOf(() => Enumerable.ThenByDescending(default(IOrderedEnumerable<object>), default(Func<object, object>))) .GetGenericMethodDefinition(); public static MethodInfo MethodOf< T >(Expression<Func< T >> method) { MethodCallExpression mce = (MethodCallExpression) method.Body; MethodInfo mi = mce.Method; return mi; } } public static class Sample { private static void Main() { var data = new List<Customer> { new Customer {ID = 3, Name ="a"}, new Customer {ID = 3, Name ="c"}, new Customer {ID = 4}, new Customer {ID = 3, Name ="b"}, new Customer {ID = 2} }; var result = data.BuildOrderBys( new SortDescription("ID", ListSortDirection.Ascending), new SortDescription("Name", ListSortDirection.Ascending) ).Dump(); } } public class Customer { public int ID { get; set; } public string Name { get; set; } } |
LinqPad中显示的样本结果
我不确定你是如何通过反射添加顺序(太懒于检查)但这里是伪代码的基本思想:
1 2 3 4 5 6 | var query = list.OrderBy(properties.First()); bool first = true; foreach(var property in properties.Skip(1)) { query = query.ThenBy(property); } |
你可以使用这样的东西:
1 2 3 4 5 6 7 | var query = _list.OrderBy(x=>x.prop1); if (shouldOrderByProp2Too) query = query.ThenBy(x=>x.prop2); if (shouldOrderByProp3Too) query = query.ThenBy(x=>x.prop3); // ... // then use query the way you had your code |
对于你的评论:
在这种情况下,我会在列表中的对象上使用一个实现