Возможно ли включить IEnumerable в IOrderedEnumerable без использования OrderBy?

Скажем, что существует метод расширения для заказа IQueryable на основе нескольких типов сортировки (т.е. сортировки по различным свойствам), обозначенных перечислением SortMethod.

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

В случае, когда SortMethod является null (т.е. там, где указано, что меня не интересует порядок значений), существует ли способ вместо упорядочения с помощью некоторого свойства по умолчанию, вместо этого просто передать значения IEnumerator через "упорядоченные" без необходимости выполнять фактический сортировку?

Мне нужна возможность вызвать это расширение, а затем, возможно, выполнить некоторые дополнительные ThenBy упорядочения.

3 ответа

Все, что вам нужно сделать для случая по умолчанию:

queryRes = values.OrderBy(a => 1);

Это будет эффективно сортировать noop. Поскольку 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();
 }
}
</t></tkey></t,></tkey></t></t></t></t></t>

С этим ваш запрос может быть:

queryRes = new NoopOrder<aclass>(values);
</aclass>

Обратите внимание, что следствием вышеприведенного класса является то, что если есть вызов ThenBy, то ThenBy будет эффективно быть сортировкой верхнего уровня. Фактически он превращает последующий ThenBy в вызов OrderBy. (Это не должно удивлять: ThenBy вызовет метод CreateOrderedEnumerable, и там этот код вызывает OrderBy, в основном превращая это ThenBy в OrderBy. С точки зрения концептуальной сортировки это это способ сказать, что "все элементы в этой последовательности равны в глазах такого рода, но если вы укажете, что равные объекты должны быть привязаны чем-то другим, тогда сделайте это.

Другой способ мышления "no op sort" заключается в том, что он упорядочивает элементы, основанные на индексе входной последовательности. Это означает, что элементы не все "равны", это означает, что последовательность ввода порядка будет окончательным порядком выходной последовательности, и поскольку каждый элемент во входной последовательности всегда больше, чем предыдущий, добавляет дополнительный "тай-брейк" "сравнения ничего не сделают, делая любые последующие вызовы 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();
 }
}
</t></t></tkey></t,></tkey></t></t></t></t></t>


Если вы всегда возвращаете одно и то же значение индекса, вы получите IOrderedEnumerable, который сохранит исходный порядок списка:

case null:
 queryRes = values.OrderBy(a => 1);
 break;

Btw Я не думаю, что это правильная вещь. Вы получите коллекцию, которая должна быть заказана, но на самом деле это не так.


В нижней строке IOrderedEnumerable существует только для обеспечения структуры грамматики методам OrderBy()/ThenBy(), что предотвращает попытку запустить предложение ordering с помощью ThenBy(). обработать. Он не предназначен для "маркера", который идентифицирует коллекцию как упорядоченную, если только она не была заказана OrderBy(). Таким образом, ответ заключается в том, что если метод сортировки, являющийся нулевым, должен указывать, что перечисляемый находится в некотором "порядке по умолчанию", вы должны указать этот порядок по умолчанию (как это делает ваша текущая реализация). Нечестно заявить, что перечислимое упорядочено, когда на самом деле это не так, даже если, не указав метод SortingMethod, вы вывели его "упорядоченным ничем" и не заботитесь о фактическом порядке.

"Проблема", присущая попытке просто пометить коллекцию как упорядоченную с использованием интерфейса, заключается в том, что для процесса больше, чем просто сортировки. Выполняя цепочку методов заказа, например myCollection.OrderBy().ThenBy().ThenByDescending(), вы фактически не сортируете коллекцию с каждым вызовом; пока еще нет. Вместо этого вы определяете поведение класса "итератор" с именем OrderedEnumerable, который будет использовать прогнозы и сравнения, которые вы определяете в цепочке, для выполнения сортировки в тот момент, когда вам нужен фактический отсортированный элемент.

Сервисный ответ, в котором указано, что OrderBy (x = > 1) является noop и должен быть оптимизирован из поставщиков SQL, игнорирует реальность, что этот вызов, сделанный против Enumerable, будет по-прежнему работать немного, и что большинство Поставщики SQL фактически не оптимизируют этот вид вызова; OrderBy (x = > 1) в большинстве поставщиков Linq создает запрос с предложением ORDER BY 1, которое не только заставляет поставщика SQL выполнять свою собственную сортировку, но и приведет к изменению порядка, потому что в T-SQL по крайней мере "ORDER BY 1" означает упорядочить по первому столбцу списка выбора.

licensed under cc by-sa 3.0 with attribution.