【问题标题】:build an expression with multiple sorting构建具有多重排序的表达式
【发布时间】:2013-01-12 13:28:27
【问题描述】:

我正在尝试构建一个用于排序的表达式,并且我编写了使用一个属性对列表进行排序的代码。

但我需要先按一个属性排序,然后再按另一个属性排序,依此类推。

我的意思是我想构建一个表达式来实现类似的东西:students.OrderBy(fistExpression.Compile()).ThenBy(secondImpression.Complie()).ThenBy(thirdExpression.Compile())。

那么如何动态放置ThenBy 方法呢?

这是我的代码:

Type studentType = typeof(Student);
ParameterExpression studentParam = Expression.Parameter(studentType, "x");
MemberInfo ageProperty = studentType.GetProperty("Age");
MemberExpression valueInNameProperty = 
     Expression.MakeMemberAccess(studentParam, ageProperty);
Expression<Func<Student, int>> orderByExpression =
     Expression<Func<Student, int>>.Lambda<Func<Student, int>>(valueInNameProperty, studentParam);
var sortedStudents = students.OrderBy(orderByExpression.Compile());

【问题讨论】:

标签: c# linq lambda expression


【解决方案1】:

我的解决方案:

public static Func<Student, object> BuildPredicate(string propertyName)
{
    Type studentType = typeof(Student);
    ParameterExpression studentParam = Expression.Parameter(studentType, "x");
    MemberInfo ageProperty = studentType.GetProperty(propertyName);
    MemberExpression valueInNameProperty = Expression.MakeMemberAccess(studentParam, ageProperty);
    UnaryExpression expression = Expression.Convert(valueInNameProperty, typeof (object));
    Expression<Func<Student, object>> orderByExpression = Expression.Lambda<Func<Student, object>>(expression, studentParam);
    return orderByExpression.Compile();
}

在您的表达式制作代码中添加了转换为object。

这就是你可以创建ThenBy链的方法:

var sortedStudents = students.OrderBy(BuildPredicate("Age"));
foreach (var property in typeof(Student).GetProperties().Where(x => !String.Equals(x.Name, "Age")))
{
    sortedStudents = sortedStudents.ThenBy(BuildPredicate(property.Name));
}

var result = sortedStudents.ToList();

最后,Student 示例类:

public class Student
{
    public int Age { get; set; }
    public string Name { get; set; } 
}

更新:

另一种方法是使用属性来标记Student 中的属性,以便在OrderBy 和ThenBy 中使用它们。喜欢:

public class Student
{
    [UseInOrderBy]
    public int Age { get; set; }

    [UseInOrderBy(Order = 1)]
    public string Name { get; set; } 
}

[AttributeUsage(AttributeTargets.Property)]
internal class UseInOrderByAttribute : Attribute
{
    public int Order { get; set; }
}

这就是你可以使用UseInOrderByAttribute构建排序链的方法:

Type studentType = typeof (Student);
var properties = studentType.GetProperties()
                            .Select(x => new { Property = x, OrderAttribute = x.GetCustomAttribute<UseInOrderByAttribute>() })
                            .Where(x => x.OrderAttribute != null)
                            .OrderBy(x => x.OrderAttribute.Order);

var orderByProperty = properties.FirstOrDefault(x => x.OrderAttribute.Order == 0);
if (orderByProperty  == null)
    throw new Exception("");

var sortedStudents = students.OrderBy(BuildPredicate(orderByProperty.Property.Name));
foreach (var property in properties.Where(x => x.Property.Name != orderByProperty.Property.Name))
{
    sortedStudents = sortedStudents.ThenBy(BuildPredicate(property.Property.Name));
}

var result = sortedStudents.ToList();

修复:BuildPredicate 可以在没有dynamic 的情况下写入。 BuildPredicate 示例代码已更改。

【讨论】:

    【解决方案2】:

    我假设您有想要排序的私有属性。 例如,如果你有这个类:

    public class Student 
    {
        public Student (int age, string name)
        {
            Age = age;
            Name = name;
        }
    
        private string Name { get;  set; }
    
        public int Age { get; set; }
    
        public override string ToString ()
        {
            return string.Format ("[Student: Age={0}, Name={1}]", Age, Name);
        }
    }
    

    您可以使用以下方法构建将获取公共和私有属性的表达式:

    public static Func<TType, TResult> CreateExpression<TType, TResult>(string propertyName) 
    {
        Type type = typeof(TType);
        ParameterExpression parameterExpression = Expression.Parameter(type, propertyName);
        MemberInfo property = type.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
        MemberExpression valueInProperty =  Expression.MakeMemberAccess(parameterExpression, property);
    
        return Expression.Lambda<Func<TType,TResult>>(valueInProperty, parameterExpression).Compile();
    }
    

    使用示例:

            var students = new [] { 
                new Student(20, "Ben"), 
                new Student(20, "Ceasar"),
                new Student(20, "Adam"),
                new Student(21, "Adam"),
            };
    
            var sortedStudents = students
                .OrderBy(CreateExpression<Student, string>("Name"))
                .ThenBy(CreateExpression<Student, int>("Age"));
    
            sortedStudents.ToList().ForEach(student => Console.WriteLine(student)); 
            /*
            Prints:
            [Student: Age=20, Name=Adam]
            [Student: Age=21, Name=Adam]
            [Student: Age=20, Name=Ben]
            [Student: Age=20, Name=Ceasar]
             */
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-08-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多