Можно ли превратить IEnumerable в IOrderedEnumerable без использования OrderBy?
Скажем, есть метод расширения для заказа IQueryable на основе нескольких типов сортировки (т.е. сортировки по различным свойствам), обозначенных SortMethod
ENUM.
public static IOrderedEnumerable<AClass> OrderByX(this IQueryable<AClass> values,
SortMethod? sortMethod)
{
IOrderedEnumerable<AClass> queryRes = null;
switch (sortMethod)
{
case SortMethod.Method1:
queryRes = values.OrderBy(a => a.Property1);
break;
case SortMethod.Method2:
queryRes = values.OrderBy(a => a.Property2);
break;
case null:
queryRes = values.OrderBy(a => a.DefaultProperty);
break;
default:
queryRes = values.OrderBy(a => a.DefaultProperty);
break;
}
return queryRes;
}
В случае, когда sortMethod
является null
(т. е. там, где указано, что мне не важен порядок значений), есть ли способ вместо упорядочения по какому-либо свойству по умолчанию вместо того, чтобы просто передать IEnumerator
значения как "упорядоченные" без необходимости выполнять фактическую сортировку?
Я хотел бы иметь возможность вызвать это расширение, а затем, возможно, выполнить некоторые дополнительные ThenBy
упорядоченности.
3 ответа
Все, что вам нужно сделать для случая по умолчанию:
queryRes = values.OrderBy(a => 1);
По сути это будет неоправданный вид. Поскольку OrderBy выполняет стабильную сортировку, исходный порядок будет поддерживаться в случае совпадения выбранных объектов. Обратите внимание, что, поскольку это IQueryable
и не IEnumerable
поставщик запросов не может выполнить стабильную сортировку. В этом случае вам нужно знать, важно ли поддерживать порядок, или уместно просто сказать: "Мне все равно, какой порядок будет в результате, пока я могу позвонить". ThenBy
на результат).
Еще один вариант, позволяющий избежать реальной сортировки, - создать свой собственный. IOrderedEnumerable
реализация:
public class NoopOrder<T> : IOrderedEnumerable<T>
{
private IQueryable<T> source;
public NoopOrder(IQueryable<T> source)
{
this.source = source;
}
public IOrderedEnumerable<T> CreateOrderedEnumerable<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer, bool descending)
{
if (descending)
{
return source.OrderByDescending(keySelector, comparer);
}
else
{
return source.OrderBy(keySelector, comparer);
}
}
public IEnumerator<T> GetEnumerator()
{
return source.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return source.GetEnumerator();
}
}
С этим ваш запрос может быть:
queryRes = new NoopOrder<AClass>(values);
Обратите внимание, что следствием вышеприведенного класса является то, что если есть вызов ThenBy
тот ThenBy
будет эффективно сортировать на высшем уровне. Это в действительности поворачивает последующий ThenBy
в OrderBy
вызов. (Это не должно удивлять; ThenBy
позвоню CreateOrderedEnumerable
метод, и там этот код вызывает OrderBy
, в основном превращая это ThenBy
в OrderBy
, С концептуальной точки зрения сортировки это способ сказать, что "все элементы в этой последовательности равны в глазах такого рода, но если вы укажете, что равные объекты должны быть разбиты на что-то еще, сделайте это.
Другой способ думать об отсутствии сортировки - это то, что он упорядочивает элементы на основе индекса входной последовательности. Это означает, что элементы не все "равны", это означает, что последовательность ввода порядка будет конечным порядком последовательности вывода, и, поскольку каждый элемент в последовательности ввода всегда больше, чем предыдущий, добавляется дополнительный "прерыватель связей". "Сравнения ничего не сделают, делая любые последующие ThenBy
звонки бессмысленные. Если такое поведение желательно, его даже проще реализовать, чем предыдущее:
public class NoopOrder<T> : IOrderedEnumerable<T>
{
private IQueryable<T> source;
public NoopOrder(IQueryable<T> source)
{
this.source = source;
}
public IOrderedEnumerable<T> CreateOrderedEnumerable<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer, bool descending)
{
return new NoopOrder<T>(source);
}
public IEnumerator<T> GetEnumerator()
{
return source.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return source.GetEnumerator();
}
}
Если вы всегда возвращаете одно и то же значение индекса, вы получите IOrderedEnumerable, который сохраняет исходный порядок списка:
case null:
queryRes = values.OrderBy(a => 1);
break;
Кстати, я не думаю, что это правильно. Вы получите коллекцию, которую предполагается заказать, но на самом деле это не так.
Суть в том, что IOrderedEnumerable существует исключительно для предоставления грамматической структуры методам OrderBy()/ThenBy(), не позволяя вам попытаться запустить предложение упорядочения с помощью ThenBy(). процесс. Он не предназначен для того, чтобы быть "маркером", который идентифицирует коллекцию как упорядоченную, если только она не была фактически упорядочена OrderBy(). Таким образом, ответ заключается в том, что если метод сортировки, имеющий нулевое значение, должен указывать, что перечислимый находится в некотором "порядке по умолчанию", вы должны указать этот порядок по умолчанию (как это делает ваша текущая реализация). Ненормально утверждать, что перечислимый порядок упорядочен, хотя на самом деле это не так, даже если, не указав SortingMethod, вы делаете вывод, что он "заказан ничем", и вам нет никакого дела до фактического порядка.
"Проблема", присущая попытке просто пометить коллекцию как упорядоченную с помощью интерфейса, заключается в том, что в процессе есть нечто большее, чем простая сортировка. Выполняя цепочку методов заказа, такую как myCollection.OrderBy().ThenBy().ThenByDescending()
вы фактически не сортируете коллекцию с каждым вызовом; все равно пока нет. Вместо этого вы определяете поведение класса "итератор" с именем OrderedEnumerable, который будет использовать проекции и сравнения, которые вы определяете в цепочке, для выполнения сортировки в тот момент, когда вам нужен фактический отсортированный элемент.
Ответ Servy, утверждающий, что OrderBy(x=>1) является ноль и должен быть оптимизирован из провайдеров SQL, игнорирует реальность того, что этот вызов, сделанный против Enumerable, все равно будет выполнять довольно много работы, и что большинство провайдеров SQL в факт не оптимизировать этот вид вызова; В большинстве поставщиков Linq OrderBy(x=>1) создает запрос с предложением "ORDER BY 1", которое не только заставляет поставщика SQL выполнять собственную сортировку, но и фактически приводит к изменению порядка, потому что в T-SQL по крайней мере "ORDER BY 1" означает упорядочение по первому столбцу списка выбора.