Как Linq использует методы IEnumerable после метода IOrderedEnumerable? - PullRequest
3 голосов
/ 13 октября 2009

В 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, чтобы увидеть, перекомпилировал ли компилятор какую-либо из операций, но, похоже, этого не произошло. Как это работает?

1 Ответ

9 голосов
/ 13 октября 2009

IOrderedEnumerable<T> extends 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);
...