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

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);
Другие вопросы по тегам