Spongman
Spongman

Reputation: 9881

Inconsistent return types of linq queries with orderby clauses

Does anyone know why the types of the result of these two queries is different?

// q1 is IOrderedEnumerable<int>
var q1 = from c1 in new[] { 1, 2, 3 }
         orderby c1
         select c1;

// q2 is IEnumerable<int>
var q2 = from c1 in new[] { 1, 2, 3 }
         from c2 in new[] { 1, 2, 3 }
         where c1 == c2
         orderby c1
         select c1;

I cannot work out why q2 is not also an IOrderedEnumerable<int>.

Using a 'join' clause doesn't make a difference:

// q3 is IEnumerable<int>
var q3 = from c1 in new[] { 1, 2, 3 }
         join c2 in new[] { 1, 2, 3 }
         on c1 equals c2
         orderby c1
         select c1;

Upvotes: 4

Views: 110

Answers (2)

Servy
Servy

Reputation: 203837

In the very first query there is no actual Select operating being performed. The select is selecting the current item in the sequence, which is a no op, so the Select call is simply omitted. With the Select call omitted, the OrderBy is the last call in the query, and it returns an IOrderedEnumerable<T>.

For the second (and third) queries, the select is actually selecting out something meaningful, and can't be omitted. (In the second query the SelectMany is going to return an IEnumerable of an anonymous type, as does the Join in the third query.) So in these cases the Select is still there in the query, and Select returns IEnumerable<T>.

It's easy enough when you look at what the queries are translated to, which will be the moral equivalent of the following:

var q1a = new[] { 1, 2, 3 }.OrderBy(c1 => c1);

var q2a = new[] { 1, 2, 3 }.SelectMany(c1 => new[] { 1, 2, 3 }.Select(c2 => new { c1, c2 }))
    .Where(variables => variables.c1 == variables.c2)
    .OrderBy(variables => variables.c1)
    .Select(variables => variables.c1);

var q3a = new[] { 1, 2, 3 }.Join(new[] { 1, 2, 3 }, c1 => c1, c2 => c2, (c1, c2) => new { c1, c2 })
    .OrderBy(variables => variables.c1)
    .Select(variables => variables.c1);

Given that these are what your queries are equivalent to, it should be clear why only the first one returns an IOrderedEnumerable<int>.

Upvotes: 5

James Curran
James Curran

Reputation: 103535

Now the actual types returned (rather that the declared interfaces) are:

q1: OrderedEnumerable<int, int>

q2: Enumerable+WhereSelectEnumerableIterator<AnonymousType<int, int>, int>

(q3 same as q2)

Upvotes: -3

Related Questions