Страницы

Поиск по вопросам

среда, 10 октября 2018 г.

Сортировка List по нескольким полям c учётом приоритета

Имеется 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)


Ответ

Примерно так:
public static IEnumerable Sort(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) без медленного рефлекшена.

Комментариев нет:

Отправить комментарий