Como o LINQ usa métodos ienumeráveis após um método intensificável?
-
21-09-2019 - |
Pergunta
Em LINQ, métodos de extensão como Where
devolver um IEnumerable
coleção, mas métodos de classificação como OrderBy
devolver um IOrderedEnumerable
coleção.
Então, se você tem uma consulta que termina com OrderBy
(ou seja, retorna um IOrderedEnumerable
), você não pode mais tarde anexar um Where
Método - o compilador reclama do tipo que está sendo transmitido Where
.
var query = Process.GetProcesses()
.Where(p => p.ProcessName.Length < 10)
.OrderBy(p => p.Id);
query = query.Where(p => p.ProcessName.Length < 5);
No entanto, se você fizer tudo isso em uma consulta, tudo bem!
var query = Process.GetProcesses()
.Where(p => p.ProcessName.Length < 10)
.OrderBy(p => p.Id)
.Where(p => p.ProcessName.Length < 5);
Eu olhei para a Assembléia no Reflector para ver se o compilador estava reordenando alguma das operações, mas não parece ter. Como é que isso funciona?
Solução
IOrderedEnumerable<T>
estende -se IEnumerable<T>
Portanto, você ainda pode usar qualquer um dos métodos de extensão. O motivo pelo qual seu primeiro bloco de código não funcionou é porque você escreveu efetivamente:
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);
Isso falha porque query.Where(...)
Retorna apenas um IEnumerable<Process>
, que não pode ser atribuído ao query
variável. Não está ligando Where
Esse é o problema - ele está atribuindo o resultado de volta à variável original. Para demonstrar isso, esse código funcionará bem:
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);
Como alternativa, você pode declarar a consulta como IEnumerable<T>
começar com:
IEnumerable<Process> query = Process.GetProcesses()
.Where(p => p.ProcessName.Length < 10)
.OrderBy(p => p.Id);
// Fine
query = query.Where(p => p.ProcessName.Length < 5);