List<T> orderby 的动态链接 [重复]
Posted
技术标签:
【中文标题】List<T> orderby 的动态链接 [重复]【英文标题】:Dynamic chaining of List<T> orderby [duplicate] 【发布时间】:2012-03-12 07:21:06 【问题描述】:我正在编写一个列表排序扩展方法。 我的输入是列表和带有属性名称和排序方向的字符串。 该字符串可以具有多个属性,如下所示: “名称 ASC、日期 DESC”等。
我已经实现了字符串解析并使用反射从字符串中获取属性本身,但我现在坚持的是如何动态链接 orderby 方法。
类似:
_list.orderBy(x=>x.prop1).thenBy(x=>x.prop2)
等
有没有办法动态构建它?
【问题讨论】:
见:***.com/a/233505/861716 【参考方案1】:使用反射从字符串属性名中获取一个 PropertyInfo 的。然后,您可以使用 PropertyInfo 构建表达式树来动态构建所有 orderby。获得表达式树后,将其编译为委托,(例如 Func,IEnumerable>)将您的 _list 参数传递给此委托,它会将排序结果作为另一个枚举提供给您。
要获取 Enumerable 上的泛型方法的反射信息,请查看此帖子的答案: Get a generic method without using GetMethods
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中显示的样本结果
【讨论】:
在我的示例中,我递归地构建一个表达式并编译每个属性,但是您可以使用 Expression.Block 构建整个表达式树,然后只编译一次。 如果您想让某些属性降序排列,您可能还想扩展此示例以包含 bool[] 升序参数。 您的解决方案看起来非常好。谢谢你。只是一件事,您对集成对 ASC 和 DESC 排序的支持有什么想法吗?我又添加了两个 MethodInfo,但我找不到如何继续前进。 我根据您的要求更新了样本。 properties 参数现在是 System.ComponentModel.SortDescription[]。它包含属性名称和订单方向。【参考方案2】:我不确定您如何通过反射添加订单(懒得检查),但这是伪代码的基本思想:
var query = list.OrderBy(properties.First());
bool first = true;
foreach(var property in properties.Skip(1))
query = query.ThenBy(property);
【讨论】:
【参考方案3】:你可以这样使用:
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
对于您的评论:
在这种情况下,我会在列表中的对象上实现IComparable
,然后对其进行排序/或使用动态检查的 Compareres - 我能想到的唯一另一件事是使用反射和动态调用。 ..如果不是真的有必要,你不想做的事情;)
【讨论】:
感谢您的回答,但如果可能的话,我更喜欢适用于任何 N 个属性的方法。以上是关于List<T> orderby 的动态链接 [重复]的主要内容,如果未能解决你的问题,请参考以下文章