在 "complex types" 中使用 LambdaExpression 对列表进行排序

Sort lists by using LambdaExpression in "complex types"

各位。在过去的几个小时里,我尝试使用 Google 找到答案,并使用即时 Window 进行绝望的测试,但我认为我没有提出正确的问题。

我的应用程序中有一个 OrderByExtender,我用它来进行排序,只使用 属性 名称和一个布尔值来判断它是 ASC 还是 DESC 排序。

    public static IOrderedEnumerable<T> OrderBy<T>(this IEnumerable<T> collection, string key, bool isAscending)
    {
        LambdaExpression sortLambda = BuildLambda<T>(key);

        if (isAscending)
            return collection.OrderBy((Func<T, object>)sortLambda.Compile());
        else
            return collection.OrderByDescending((Func<T, object>)sortLambda.Compile());
    }

    private static LambdaExpression BuildLambda<T>(string key)
    {
        ParameterExpression TParameterExpression = Expression.Parameter(typeof(T), "p");
        LambdaExpression sortLambda = Expression.Lambda(Expression.Convert(Expression.Property(TParameterExpression, key), typeof(object)), TParameterExpression);
        return sortLambda;
    }

当我使用通用类型属性(字符串、整数等)时,它就像一个魅力。但现在我想到了以下场景:我有一个名为 BusinessOrder 的对象,在它里面有一个类型为 Quarter[=27] 的 属性 =].这个 Quarter 对象具有三个属性:Year(int)、Quarter(int) 和 Abbreviation(string)。我必须使用缩写 属性 执行 OrderBy。换句话说,我必须这样做:

BusinessOrderList.OrderBy(b => b.Quarter.Abbreviation);

但我想将这种排序可能性放入我的 Extender 中,通过作为 key 参数传递 "Quarter.Abbreviation" 之类的东西并且 Extender 方法理解这是一个问题在 "complex object".

中获取 属性

我相信我可以在创建 sortLambda 变量的 "Expression.Lambda" 方法中做一些事情,但我不知道如何使用表达式复制此行为.有人能帮我吗?提前致谢。

您无法一次访问复杂的 属性。您必须递归地构建表达式链:

    private Func<TInput, object> BuildLambda<TInput>(string complexPropertyPath)
    {
        var parameter = Expression.Parameter(typeof(TInput), "p");

        var propertyPathParts = complexPropertyPath.Split('.');
        MemberExpression complexPropertyAccessExpression = null;
        foreach (var propertyPathPart in propertyPathParts)
        {
            complexPropertyAccessExpression = complexPropertyAccessExpression == null
                ? Expression.Property(parameter, propertyPathPart)
                : Expression.Property(complexPropertyAccessExpression, propertyPathPart);
        }

        var lambda = (Func<TInput, object>)Expression.Lambda(complexPropertyAccessExpression, parameter).Compile();

        return lambda;
    }

如果可能,我建议将 string key 参数替换为 Expression<Func<T, object>> key 参数。这样,您的函数调用就可以像您描述的那样 BusinessOrderList.OrderBy(b => b.Quarter.Abbreviation); 。由于所有 OrderBy/OrderByDescending 方法都接受此表达式作为字符串的替代方法,所以我看不出将 key 保留为字符串有什么好处。

您可以传递 Func 来对您的 collection

进行排序
public static IOrderedEnumerable<T> OrderBy<T>(this IEnumerable<T> collection,
                           Func<IEnumerable<T>,IOrderedEnumerable<T>> OrderBy)
{
    if(OrderBy != null)
        return OrderBy(collection);
    return collection; 
}

你必须这样称呼它

{
    Func<IEnumerable<Book>,IOrderedEnumerable<Book>> sort = 
    list => list.OrderBy(B=>B.Author.Name).ThenByDescending(B=>B.Title) ; 

    List.OrderBy(sort);

}