Как Linq использует методы IEnumerable после метода IOrderedEnumerable?

В Linq методы расширения вроде Whereвозвращают IEnumerableколлекцию, а методы сортировки OrderByвозвращают IOrderedEnumerableколлекцию.

Таким образом, если у вас есть запрос, который заканчивается OrderBy(то есть возвращает IOrderedEnumerable), вы не можете позже добавить Whereметод - компилятор жалуется на тип, в который передается Where.

var query = Process.GetProcesses()
            .Where(p => p.ProcessName.Length < 10)
            .OrderBy(p => p.Id);

query = query.Where(p => p.ProcessName.Length < 5);

Однако, если вы делаете все это в одном запросе, это нормально!

var query = Process.GetProcesses()
            .Where(p => p.ProcessName.Length < 10)
            .OrderBy(p => p.Id)
            .Where(p => p.ProcessName.Length < 5);

Я посмотрел на сборку в Reflector, чтобы увидеть, перекомпилировал ли компилятор какую-либо из операций, но, похоже, этого не произошло. Как это работает?

13.10.2009 09:22:21
1 ОТВЕТ
РЕШЕНИЕ

IOrderedEnumerable<T>расширяется, IEnumerable<T>так что вы все еще можете использовать любой из методов расширения. Причина, по которой ваш первый блок кода не сработал, заключается в том, что вы эффективно написали:

IOrderedEnumerable<Process> query = Process.GetProcesses()
                                           .Where(p => p.ProcessName.Length < 10)
                                           .OrderBy(p => p.Id);

// Fail: can't assign an IEnumerable<Process> into a variable 
// of type IOrderedEnumerable<Process>
query = query.Where(p => p.ProcessName.Length < 5);

Это терпит неудачу, потому что query.Where(...)только возвращает IEnumerable<Process>, который не может быть назначен queryпеременной. WhereПроблема не в вызове , а в присвоении результата исходной переменной. Чтобы продемонстрировать это, этот код будет отлично работать:

var query = Process.GetProcesses()
                   .Where(p => p.ProcessName.Length < 10)
                   .OrderBy(p => p.Id);

// Introduce a new variable instead of trying to reuse the previous one
var query2 = query.Where(p => p.ProcessName.Length < 5);

Кроме того, вы можете объявить запрос, IEnumerable<T>чтобы начать с:

IEnumerable<Process> query = Process.GetProcesses()
                                    .Where(p => p.ProcessName.Length < 10)
                                    .OrderBy(p => p.Id);

// Fine
query = query.Where(p => p.ProcessName.Length < 5);
9
13.10.2009 09:32:07
Да, но в чем разница между двумя примерами? Почему второй работает, а первый нет?
Graham Clark 13.10.2009 09:34:43
Второй не пытается присвоить IEnumerable<T>выражение переменной типа IOrderedEnumerable<T>.
Jon Skeet 13.10.2009 09:35:57