How does Linq use IEnumerable methods after IOrderedEnumerable method?
In Linq, extension methods such as Where
return a collection IEnumerable
, but sorting methods such as OrderBy
return a collection IOrderedEnumerable
.
So, if you have a query ending in OrderBy
(i.e. returns IOrderedEnumerable
), you cannot add the method later Where
- the compiler complains about the type being passed to 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're doing it all in one request, that's great!
var query = Process.GetProcesses()
.Where(p => p.ProcessName.Length < 10)
.OrderBy(p => p.Id)
.Where(p => p.ProcessName.Length < 5);
I looked at the assembly in Reflector to see if the compiler had recompiled any operations, but it doesn't seem to be the case. How it works?
source to share
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 effectively wrote:
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);
This fails because it query.Where(...)
only returns IEnumerable<Process>
which cannot be assigned to a variable query
. It doesn't cause Where
that the problem is assigning the result back to the original variable. To demonstrate this, this code will work very well:
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 a request 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);
source to share