Как Linq использует методы IEnumerable после метода IOrderedEnumerable?
-
21-09-2019 - |
Вопрос
В 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, чтобы узнать, переупорядочивал ли компилятор какие-либо операции, но, похоже, этого не произошло.Как это работает?
Решение
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);