Come Linq usa metodi IEnumerable dopo un metodo IOrderedEnumerable?
-
21-09-2019 - |
Domanda
In LINQ, i metodi di estensione come Where
restituiscono una collezione IEnumerable
, ma i metodi di ordinamento come OrderBy
restituiscono una collezione IOrderedEnumerable
.
Quindi, se si dispone di una query che si conclude con OrderBy
(cioè restituisce un IOrderedEnumerable
), non è possibile in seguito aggiungere un metodo di Where
- il compilatore si lamenta il tipo di essere passata in Where
var query = Process.GetProcesses()
.Where(p => p.ProcessName.Length < 10)
.OrderBy(p => p.Id);
query = query.Where(p => p.ProcessName.Length < 5);
Tuttavia, se lo si fa tutto in una query, va bene!
var query = Process.GetProcesses()
.Where(p => p.ProcessName.Length < 10)
.OrderBy(p => p.Id)
.Where(p => p.ProcessName.Length < 5);
Ho guardato l'assemblea in Reflector per vedere se il compilatore è stato ri-ordinare qualsiasi delle operazioni, ma non sembrano avere. Come funziona?
Soluzione
IOrderedEnumerable<T>
estende IEnumerable<T>
in modo da poter ancora utilizzare uno dei metodi di estensione. Il motivo per il vostro primo blocco di codice non ha funzionato è perché si aveva effettivamente scritto:
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);
che non riesce a causa query.Where(...)
restituisce solo un IEnumerable<Process>
, che non può essere assegnato alla variabile query
. Non è chiamando Where
che è il problema - è assegnare il risultato di nuovo alla variabile originale. Per dimostrare che, questo codice funziona bene:
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);
In alternativa, è possibile dichiarare query per essere IEnumerable<T>
di iniziare con:
IEnumerable<Process> query = Process.GetProcesses()
.Where(p => p.ProcessName.Length < 10)
.OrderBy(p => p.Id);
// Fine
query = query.Where(p => p.ProcessName.Length < 5);