Can these two LINQ queries be used interchangeably

2019-02-24 08:11发布

问题:

a) Would the following two queries produce the same results:

  var query1 = collection_1
            .SelectMany(c_1 => c_1.collection_2)
            .SelectMany(c_2 => c_2.collection_3)
            .Select(c_3 => c_3);

  var query2 = collection_1
            .SelectMany(c_1 => c_1.collection_2
            .SelectMany(c_2 => c_2.collection_3.Select(c_3 => c_3)));

b) I assume the two queries can't always be used interchangeably? For example, if we wanted the output elements to also contain values of c_1 and c_2, then we only achieve this with query2, but not with query1:

  var query2 = collection_1
            .SelectMany(c_1 => c_1.collection_2
            .SelectMany(c_2 => c_2.collection_3.Select(c_3 => new { c_1, c_2, c_3 } )));

?

Thank you

回答1:

The snippets you've given seem to be invalid. c_3 isn't defined in the scope of the Select statement, so unless I've misunderstood something, this won't compile.

It seems as though you're trying to select the elements of collection_3, but this is done implicitly by SelectMany, and so the final Select statements in both cases are redundant. Take them out, and the two queries are equivalent.

All you need is this:

var query = collection_1
           .SelectMany(c_1 => c_1.collection_2)
           .SelectMany(c_2 => c_2.collection_3);

Update: x => x is the identity mapping, so Select(x => x) is always redundant, regardless of the context. It just means "for every element in the sequence, select the element".

The second snippet is of course different, and the SelectMany and Select statements indeed need to be nested in order to select all three elements, c_1, c_2, and c_3.

Like Gert, says, though, you're probably better off using query comprehension syntax. It's much more succinct and makes it easier to mentally parse the workings of a query.



回答2:

a. The queries are equal because in both cases you end up with all c_3's in c_1 through c_2.

b. You can't get to c_1 and c_2 with these queries as you suggest. If you want that you need this overload of SelectMany. This "fluent" syntax is quite clumsy though. This is typically a case where comprehensive syntax which does the same is much better:

from c_1 in colection_1
from c_2 in c_1.collection_2
from c_3 in c_2.collection_3
select new { c_1.x, c_2.y, c_3.z }