Reputation: 12966
In Linq, extension methods like Where
return an IEnumerable
collection, but sorting methods like OrderBy
return an IOrderedEnumerable
collection.
So, if you have a query that ends with OrderBy
(i.e. returns an IOrderedEnumerable
), you can't later append a Where
method - the compiler complains about the type being passed into Where
.
var query = Process.GetProcesses()
.Where(p => p.ProcessName.Length < 10)
.OrderBy(p => p.Id);
query = query.Where(p => p.ProcessName.Length < 5);
However, if you do it all in one query, it's fine!
var query = Process.GetProcesses()
.Where(p => p.ProcessName.Length < 10)
.OrderBy(p => p.Id)
.Where(p => p.ProcessName.Length < 5);
I've looked at the assembly in Reflector to see if the compiler was re-ordering any of the operations, but it doesn't seem to have. How does this work?
Upvotes: 3
Views: 2392
Reputation: 1502306
IOrderedEnumerable<T>
extends IEnumerable<T>
so you can still use any of the extension methods. The reason your first block of code didn't work is because you had effectively written:
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);
That fails because query.Where(...)
only returns an IEnumerable<Process>
, which can't be assigned to the query
variable. It's not calling Where
that's the problem - it's assigning the result back to the original variable. To demonstrate that, this code will work just fine:
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);
Alternatively, you can declare query to be IEnumerable<T>
to start with:
IEnumerable<Process> query = Process.GetProcesses()
.Where(p => p.ProcessName.Length < 10)
.OrderBy(p => p.Id);
// Fine
query = query.Where(p => p.ProcessName.Length < 5);
Upvotes: 9