在“复杂类型”中使用LambdaExpression对列表进行排序

时间:2016-05-25 14:34:04

标签: c# linq sorting lambda linq-expressions

大家。在过去的几个小时里,我尝试使用立即窗口找到Google的答案和绝望的测试,但我认为我没有提出正确的问题。

我的应用程序中有一个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;
    }

当我使用common-type属性(string,int等)时,它就像一个魅力。但现在我想出了以下场景:我有一个名为 BusinessOrder 的对象,其中有一个类型为 Quarter 的属性。此Quarter对象有三个属性:Year(int),Quarter(int)和缩写(string)。我必须使用缩写属性来执行OrderBy。换句话说,我必须这样做:

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

但是我想把这种可能性放在我的Extender中,通过传递参数,例如“Quarter.Abbreviation”,Extender方法理解这是将属性放入内部的问题一个“复杂的对象”。

我相信我可以在“Expression.Lambda”方法中创建 sortLambda 变量,但我无法弄清楚如何使用Expressions复制此行为。有人能帮我吗?提前谢谢。

3 个答案:

答案 0 :(得分:2)

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

    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;
    }

答案 1 :(得分:0)

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

答案 2 :(得分:0)

您可以传递Func来整理您的收藏

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);

}