Можно ли превратить 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;
}

в случае 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();
    }
}

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

queryRes = new NoopOrder<AClass>(values);

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

другой способ мышления "без сортировки op" заключается в том, что он заказывает элементы, основанные на индекс входной последовательности. Это означает, что элементы не все "равны", это означает, что последовательность ввода будет быть конечным порядком выходной последовательности, и поскольку каждый элемент во входной последовательности всегда больше, чем перед ним, добавление дополнительных сравнений "tiebreaker" ничего не сделает, делая любые последующие 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 (), предотвращая попытку запуска предложения ordering с помощью ThenBy(). процесс. Он не предназначен для "маркера", который идентифицирует коллекцию как упорядоченную, если она не была фактически заказана OrderBy(). Таким образом, ответ заключается в том, что если метод сортировки, являющийся null, должен указывать, что перечисляемый находится в некотором "порядке по умолчанию", вы должны указать этот порядок по умолчанию (как текущая реализация делает). Неискренне утверждать, что перечисляемое упорядочено, когда на самом деле это не так, даже если, не указывая Сортировочный метод, вы делаете вывод, что он "упорядочен ничем" и не заботится о фактическом порядке.

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

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