7

ソート用の式を作成しようとしていますが、1 つのプロパティを使用してリストをソートするコードを書きました。

しかし、最初にあるプロパティで並べ替え、次に別のプロパティで並べ替える必要があります。

つまり、次のようなものを実装する式を作成したいということです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());
4

2 に答える 2

2

私の解決策:

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

アップデート:

もう1つのアプローチは、属性を使用してプロパティをマークし、およびStudentで使用することです。好き:OrderByThenBy

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なしで書き込むことができますdynamicBuildPredicateサンプルコードが変更されました。

于 2013-01-12T13:53:56.883 に答える
1

並べ替えることができるプライベートプロパティがあると思います。たとえば、このクラスがある場合:

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]
         */
于 2013-01-12T14:41:34.383 に答える