C# in Depth

Cover of C# in Depth
Order now (3rd edition)

Scoping of key selectors in joins

Chapter 11: Query expressions and LINQ to Objects: 11.5.1

Created: 3/5/2008
Last updated: 3/5/2008

I mention the "scoping issue" briefly after listing 11.12, but it's worth going into this slightly more fully. Let's consider joining two ranges of numbers together in the obvious way. This query expression is valid:

from left in Enumerable.Range(0, 10)
join right in Enumerable.Range(5, 15)
  on left equals right
select left*right

This, however, is not:

// Warning: invalid!
from left in Enumerable.Range(0, 10)
join right in Enumerable.Range(5, 15)
  on right equals left
select left*right

The left side of the equals only knows about the "main" sequence, whereas the right side only knows about the extra sequence which is being joined with the main one. We can see why it fails when the compiler translation is performed on each of the two expressions above. First the working one:

Enumerable.Range(0, 10)
          .Join(Enumerable.Range(5, 15), // Extra sequence
                left => left, // Left key selector
                right => right, // Right key selector
                (left, right) => left*right // Result selector
               )

That's fine. All the lambda expressions make sense. This is the translation of the broken query expression though:

// Warning: invalid!
Enumerable.Range(0, 10)
          .Join(Enumerable.Range(5, 15), // Extra sequence
                left => right// Left key selector
                right => left// Right key selector
                (left, right) => left*right // Result selector
               )

Neither of the key selector lambda expressions will compile, because they refer to variables which aren't available.