micahhoover
micahhoover

Reputation: 2160

linq: separate orderby and thenby statements

I'm coding through the 101 Linq tutorials from here:

http://code.msdn.microsoft.com/101-LINQ-Samples-3fb9811b

Most of the examples are simple, but this one threw me for a loop:

    [Category("Ordering Operators")]
    [Description("The first query in this sample uses method syntax to call OrderBy and ThenBy with a custom comparer to " +
                 "sort first by word length and then by a case-insensitive sort of the words in an array. " +
                 "The second two queries show another way to perform the same task.")]
    public void Linq36()
    {
        string[] words = { "aPPLE", "AbAcUs", "bRaNcH", "BlUeBeRrY", "ClOvEr", "cHeRry", "b1" };

        var sortedWords =
            words.OrderBy(a => a.Length)
                 .ThenBy(a => a, new CaseInsensitiveComparer());

        // Another way. TODO is this use of ThenBy correct? It seems to work on this sample array.
        var sortedWords2 =
            from word in words
            orderby word.Length
            select word;

        var sortedWords3 = sortedWords2.ThenBy(a => a, new CaseInsensitiveComparer());

No matter which combination of words I throw at it the length is always the first ordering criteria ... even though I don't know how the second statement (with no orderby!) knows what the original orderby clause was.

Am I going crazy? Can anyone explain how Linq "remembers" what the original ordering was?

Upvotes: 1

Views: 3951

Answers (3)

Joel Etherton
Joel Etherton

Reputation: 37543

Your question really doesn't make much sense on the surface because you're not considering the nature of the deferred execution. It doesn't "remember" in either case truthfully, it simply isn't executed until it's really needed. If you run over your examples in the debugger you will find that these generate identical (structurally anyway) statements. Consider:

var sortedWords =
        words.OrderBy(a => a.Length)
             .ThenBy(a => a, new CaseInsensitiveComparer());

You've explicitly told it to OrderBy, ThenBy. Each statement is stacked on until they're all complete, and the finally query is constructed to look like (psuedo):

 Select from sorted words, order by length, order by comparer

Then once that is all ready to go it is executed and placed into sortedWords. Now consider:

var sortedWords2 =
        from word in words
        orderby word.Length   // You're telling it to sort here
        select word;

// Now you're telling it to ThenBy here
var sortedWords3 = sortedWords2.ThenBy(a => a, new CaseInsensitiveComparer());

And then once those queries are stacked up it will be executed. However, it WON'T be executed until you NEED them. sortedWords3 won't really have any value until you act on it because the need for it is deferred. So in both cases, you're basically saying to the compiler:

  1. Wait until I'm done building my query
  2. Select from source
  3. Order by length
  4. Then by comparer
  5. Ok do your stuff.

Note: To sum up, LINQ doesn't "remember", it simply doesn't execute until you're done giving it instructions to execute. Then it stacks them up into a query and runs them all at once when they're needed.

Upvotes: 1

Servy
Servy

Reputation: 203847

The return type of OrderBy is not IEnumerable<T>. It's IOrderedEnumerable<T>. This is an object that "remembers" all of the orderings it's been given, and as long as you don't call another method that turns the variable back into an IEnumerable it will retain that knowledge.

See Jon Skeets wonderful blog series Eduling in which he re-implements Linq-to-objects for more info. The key entries on OrderBy/ThenBy are:

Upvotes: 5

ashutosh raina
ashutosh raina

Reputation: 9314

This is because LINQ is lazy, the first i.e. all the evaluation only happens when you enumerate the sequence.. the expression tree that has been constructed gets executed.

Upvotes: 1

Related Questions