26

Why does my parameter x behave so erratically?

  1. Example 1 - Doesn't exist in the current context.
  2. Example 2 - Cannot reuse x because it's defined in a 'child' scope.
  3. Example 3 - Fine. This is the part where I am confused. Perhaps a different 'child' scope?

Example 1:

List<int> list = new List<int> { 1, 2, 3, 4, 5 };
var result = list.Where(x => x < 3);
Console.Write(result.ElementAt(x));

creates this compile time error:

The name 'x' does not exist in the current context

which I expect.

Example 2:

List<int> list = new List<int> { 1, 2, 3, 4, 5 };
var result = list.Where(x => x < 3);
int x = 1;
Console.Write(result.ElementAt(x));

produces this compile-time error:

A local variable named 'x' cannot be declared in this scope because it would give a different meaning to 'x', which is already used in a 'child' scope to denote something else

I understand the scoping as answered in this question, Is there a reason for C#'s reuse of the variable in a foreach?. However, this is something I've never seen before. In addition, it makes the answers to this question, What is the scope of a lambda variable in C#?, incomplete or wrong.

Example 3:

List<int> list = new List<int> { 1, 2, 3, 4, 5 };
List<string> stringList = new List<string> { "A", "B" };
var result = list.Where(x => x < 3);
var result2 = stringList.Where(x => x != "A");

Console.Write(result2);

No errors produced.


With the accepted answer, these blog posts from Eric Lippert helped me wrap my head around what was happening. If anyone is still confused:

declaration space

simple names

Community
  • 1
  • 1
Joe
  • 80,724
  • 18
  • 127
  • 145
  • 1
    [Here](http://blogs.msdn.com/b/ericlippert/archive/2009/11/02/simple-names-are-not-so-simple.aspx) are two relevant [links](http://blogs.msdn.com/b/ericlippert/archive/2009/11/05/simple-names-are-not-so-simple-part-two.aspx) on the subject. – Servy May 14 '12 at 17:12
  • possible duplicate of [local variable scope in linq anonymous method ( closure)](http://stackoverflow.com/questions/10517964/local-variable-scope-in-linq-anonymous-method-closure) – Magnus May 14 '12 at 17:14
  • possible duplicate of [What is the scope of a lambda variable in C#?](http://stackoverflow.com/questions/10494074/what-is-the-scope-of-a-lambda-variable-in-c) – nawfal Jan 29 '14 at 10:25

3 Answers3

21

In Example 1, x is defined in the local scope of the lamdba expression and is not visible to the third line

In Example 2, now you've declared two variables named "x" at the same declaration scope (visibility is different)

With a lambda or anonymous method, it "captures" the scope at which it is running. If you have a local x in the same scope as the lambda definition, then it "captures" that x to pull into what the lambda can access--thus resulting in two definitions of "x". What you declare in the lambda doesn't get captured in the other direction so it isn't visible outside the lambda.

In Example 3, Now you're not using a variable that is local only the lambda outside the lambda, and not naming something the same at the same declaration scope.

Peter Ritchie
  • 35,463
  • 9
  • 80
  • 98
  • 1
    I still don't get, how/why example 2 does not compile. If ex-2 won't then even ex-3 should not compile as well. As per my logic, the 2nd declaration of x "int x" should not have any concern with the 1st one, as it is explicitly defined and available only for the lambda expression defined on that line. This is so damn new and strange for me. – MrClan May 14 '12 at 18:21
  • 1
    @MrClan look at [this code](http://pastebin.com/DvDnNaPz) it is similar to the scoping issue and it may help you understand. – Scott Chamberlain May 14 '12 at 19:32
  • 3
    @MrClan it doesn't compile because the "x => ..." declare one x variable, and the "int x =1;" declares another. The lambda is a closure that can bring in "outer variables" that are captured for when the lambda is executed. In order for those variables to be captured they need to be unique. "x" could mean the x defined in the lambda, or the x defined in the outer scope--so, the compiler doesn't know which one to use in the "x < 3" code... – Peter Ritchie May 14 '12 at 22:03
5

Child scopes (example 3) can use the same variables, but parent and child can't re-declare variables.

You can get the same with for:

// Child scopes
for (int i = 1; i < 10; i++){ /* do something */ }
for (int i = 1; i < 10; i++){ /* do something else */ }

This would fail :

// Child and parent
for (int i = 1; i < 10; i++){ /* do something */ }
int i = 33;
Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
3

It is not so complicated as it seems to be.

If you define a parameter for a lamda expression, the parameter is only valid inside the scope of the lamda expression

(int x) =>
{
   //x is only valid inside this scope
}

If you have a second variable with the defined in the same scope as the lamda expression, you will get an error, because this second variable is also valid in the scope of the lamda expression.

void Foo()
{

 int y;

 //y is valid in the scope of foo

 (int x) =>
 {
    //x is only valid inside this scope
    //y is valid in the scope of foo and the lamda expression
 }
}

In the third example you have 2 different lamda expressions and therefore two different scopes

Manuel Amstutz
  • 1,311
  • 13
  • 33