<分区>
我正在写一个列表排序扩展方法。 我的输入是列表和一个带有属性名称和排序方向的字符串。 该字符串可以具有多个属性,如下所示: “名称升序,日期降序”等
我已经实现了字符串解析并使用反射从字符串中获取属性本身,但我现在坚持的是如何动态链接 orderby 方法。
类似于:
_list.orderBy(x=>x.prop1).thenBy(x=>x.prop2)
等
有什么方法可以动态构建它吗?
<分区>
我正在写一个列表排序扩展方法。 我的输入是列表和一个带有属性名称和排序方向的字符串。 该字符串可以具有多个属性,如下所示: “名称升序,日期降序”等
我已经实现了字符串解析并使用反射从字符串中获取属性本身,但我现在坚持的是如何动态链接 orderby 方法。
类似于:
_list.orderBy(x=>x.prop1).thenBy(x=>x.prop2)
等
有什么方法可以动态构建它吗?
最佳答案
使用反射从字符串属性名称获取 PropertyInfo。然后,您可以使用 PropertyInfo 构建表达式树以动态构建所有 orderby。获得表达式树后,将其编译为委托(delegate),(例如 Func、IEnumerable>)将您的 _list 参数传递给该委托(delegate),它将为您提供另一个可枚举的有序结果。
要获取 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中示例的结果
关于c# - List<T> orderby 的动态链接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9663090/
相关文章:
c# - 使用 C# 将方法(返回类型为 void 并且没有输入参数)作为参数传递
c# - 将事件绑定(bind)/连接到嵌套在 ItemsControls DataTemplate 中的 ItemsControl 中的按钮
c# - 使用 linq2sql/datacontext 将记录插入数据库
vb.net - 删除 linq to 实体查询中带有数字的字段
c# - DebuggerStepperBoundaryAttribute 的示例用法是什么?
c# - .NET MVC 身份验证 - 表单 + Windows 身份验证