3

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?

Graham Clark
  • 12,886
  • 8
  • 50
  • 82

1 Answers1

9

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);
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Yes, but what is the difference between the two examples? Why does the second one work but the first doesn't? – Graham Clark Oct 13 '09 at 09:34
  • 5
    The second one doesn't try to assign an `IEnumerable` expression into a variable of type `IOrderedEnumerable`. – Jon Skeet Oct 13 '09 at 09:35