Thursday, May 01, 2008

C# block scoping rules for variables, anonymous methods and lambdas

I was reading Rick Strahl's blog post about "Variable Scoping in Anonymous Delegates in C#". I added a completely wrong comment there. That happens when studying too many languages at the same time. (My only excuse is that even Rick itself wasn't entirely right.)

JavaScript, Scala, and F# allow to hide outer variable by declaring another variable with the same name in a nested scope. C# does not allow to do that!

In C# lambdas and anonymous methods are treated as inline code placed into a nested block scope. It is consistent with the fact that they can access local variables from outer scope.
Behind the scenes compiler activities of creating actual delegate object and closure behavior are what they are: behind the scenes compiler activities. They should not and do not affect lexical rules.

And those lexical scoping rules for nested blocks, anoyimous methods, and lambdas are as followed:

namespace VariableScope {
/// <summary>

/// C# 3.0 in a Nutshell, http://www.amazon.com/3-0-Nutshell-Desktop-Reference-OReilly/dp/0596527578/
/// Page 46, "The scope of local or constant variable extends to the end of the current block.
/// You cannot declare another local variable with the same name in the current block
/// or in any nested blocks."
/// That's an opposite to JavaScript and F# scoping rules.
/// </summary>

class Program {
delegate int Adder();

static void Main(string[] args) {
int x;
{
int y;
int z;
int x; // error, x already defined in outer scope (1*)
}
int y; // error, y already defined in a child scope (2*)
{
int z; // ok, no z in outer block
}
Console.WriteLine(z); // error, z is out of scope

int t, u;
Adder goodAdder = () => { return t++; }; //ok
Adder badAdder = () => { int u; return t + u; }; // error, u already defined in outer scope
Adder badToo = delegate { int u; return t + u; }; // the same error
}
}
}
// 1*: Compiler errors are as followed
// 1 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 'parent or current' scope to denote something else
//
// 2*: Compiler error is as followed
// A local variable named 'y' cannot be declared in this scope because it would give
// a different meaning to 'y', which is already used in a 'child' scope to denote something else