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

StackOverflow https://stackoverflow.com/questions/1559113

Вопрос

В 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);
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top