#c_sharp #сортировка
Имеется List, где T является каким-то классом с полями. Также имеется класс, содержащий параметры сортировки: public class Sort { public String Property { get; set; } public String Direction { get; set; } } Property указывает по какому полю будет осуществляться сортировка, а Direction указывает по возрастанию или убыванию (реально принимает значение "ASC" или "DSC") Имеется метод, с сигнатурой public static IEnumerable Sort (IEnumerable items, Sort[] sort) В массиве Sort[] чем меньше индекс элемента, тем выше его приоритет. Хочется понять как можно осуществить сортировку по нескольким полям (больше двух), притом что явно не понятно в каком направлении и какое количество полей будет использоваться. Иными словами нельзя написать items.OrderBy(...).ThenBy(...) Для извлечения свойств из типа T используется typeof(T).GetProperty(sort[i].Property)
Ответы
Ответ 1
Примерно так: public static IEnumerableSort (IEnumerable items, Sort[] sort) { IOrderedEnumerable temp = null; foreach (var s in sort) { Func keySelector = GetKeySelector (s.Property); if (temp == null) { temp = s.Direction == "Asc" ? items.OrderBy(keySelector) : items.OrderByDescending(keySelector); } else { temp = s.Direction == "Asc" ? temp.ThenBy(keySelector) : temp.ThenByDescending(keySelector); } } return temp ?? items; } private static Func GetKeySelector (string property) { var param = Expression.Parameter(typeof(T)); var lambda = Expression.Lambda >( Expression.Convert( Expression.Property(param, property), typeof(IComparable)), param); return lambda.Compile(); } можно убрать if-ы и добавить чуть-чуть динамики, вроде поиска метода ThenBy по имени - но на скорость работы это никак не повлияет. Этот метод не идеален: он упадет при попытке сравнить по свойствам с типом, не реализующим IComparable (но, например, реализующим IComparable ). Для обхода проблемы в общем случае придется или заменить в коде IComparable на object, получив боксинг при сравнении или перейти на IQueryable - свалив проблему построения фунции сравнения на провайдер Linq To Objects, как сделано в ответе Andrei. GetKeySelector - это просто способ сделать typeof(T).GetProperty(sort[i].Property) без медленного рефлекшена. Ответ 2
Кажется, это то, что вам нужно: Сам метод будет выглядеть примерно так: массив Sort нужно будет предварительно поменять, первый элемент должен быть OrderBy(OrderByDescending), остальные ThenBy(ThenByDescending) public static IEnumerableSortIt (IEnumerable items, Sort[] sorts) { var queryItems = items.AsQueryable(); foreach (var s in sorts) queryItems = queryItems.ApplyOrder(s.Property, s.Direction); return queryItems.AsEnumerable(); } Ещё нужен экстеншен: public static class Extensions { public static IOrderedQueryable ApplyOrder ( this IQueryable source, string property, string methodName ) { var arg = Expression.Parameter(typeof(T), "x"); Expression expr = arg; expr = Expression.Property(expr, property); var lambda = Expression.Lambda(expr, arg); var method = typeof(Queryable).GetGenericMethod( methodName, new[] { typeof(T), expr.Type }, new[] { source.GetType(), lambda.GetType() } ); return (IOrderedQueryable )method.Invoke(null, new object[] { source, lambda }); } public static MethodInfo GetGenericMethod( this Type type, string name, Type[] genericTypeArgs, Type[] paramTypes ) { var methods = from abstractGenericMethod in type.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static) where abstractGenericMethod.Name == name where abstractGenericMethod.IsGenericMethod let pa = abstractGenericMethod.GetParameters() where pa.Length == paramTypes.Length select abstractGenericMethod.MakeGenericMethod(genericTypeArgs) into concreteGenericMethod where concreteGenericMethod.GetParameters() .Select(p => p.ParameterType).SequenceEqual(paramTypes, new TestAssignable()) select concreteGenericMethod; return methods.FirstOrDefault(); } private class TestAssignable : IEqualityComparer { public bool Equals(Type x, Type y) { return x.IsAssignableFrom(y); } public int GetHashCode(Type obj) { return obj.GetHashCode(); } } } https://habrahabr.ru/post/181065/
Комментариев нет:
Отправить комментарий