c# - List<T> orderby 的动态链接

标签 c# linq generics

<分区>

我正在写一个列表排序扩展方法。 我的输入是列表和一个带有属性名称和排序方向的字符串。 该字符串可以具有多个属性,如下所示: “名称升序,日期降序”等

我已经实现了字符串解析并使用反射从字符串中获取属性本身,但我现在坚持的是如何动态链接 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中示例的结果

enter image description here

关于c# - List<T> orderby 的动态链接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9663090/

相关文章:

swift - Swift 如何消除泛型构造函数的歧义?

c# - 使用 C# 将方法(返回类型为 void 并且没有输入参数)作为参数传递

c# - 将事件绑定(bind)/连接到嵌套在 ItemsControls DataTemplate 中的 ItemsControl 中的按钮

c# - 使用 linq2sql/datacontext 将记录插入数据库

c# - 根据条件合并 List<T> 中的两个或多个 T

vb.net - 删除 linq to 实体查询中带有数字的字段

c# - DebuggerStepperBoundaryAttribute 的示例用法是什么?

c# - .NET MVC 身份验证 - 表单 + Windows 身份验证

c# - Sybase (SAP) ASE Ado.Net 16384 个字符的限制?

generics - 我如何要求泛型类型支持数字运算?