I found the following rather strange. Then again, I have mostly used closures in dynamic languages which shouldn\'t be suspectable to the same \"bug\". The following makes t
It's because the delegate can reference variables outside the delegate:
int i = 1;
VoidFunction t = delegate { Console.WriteLine(i); };
Actually, the error doesn't seem to have anything to do with anonymous delegates or lamda expressions. If you try to compile the following program ...
using System;
class Program
{
static void Main()
{
// Action t = delegate
{
int i = 0;
};
int i = 1;
}
}
... you get exactly the same error, no matter whether you comment in the line or not. The error help shows a very similar case. I think it is reasonable to disallow both cases on the grounds that programmers could confuse the two variables.
You'll also get CS0136 from code like this:
int i = 0;
if (i == 0) {
int i = 1;
}
The scope of the 2nd declaration of "i" is unambiguous, languages like C++ don't have any beef with it. But the C# language designers decided to forbid it. Given the above snippet, do you think still think that was a bad idea? Throw in a bunch of extra code and you could stare at this code for a while and not see the bug.
The workaround is trivial and painless, just come up with a different variable name.
The "closure" created by an anonymous function is somewhat different from that created in other dynamic languages (I'll use Javascript as an example).
function thing() {
var o1 = {n:1}
var o2 = {dummy:"Hello"}
return function() { return o1.n++; }
}
var fn = thing();
alert(fn());
alert(fn());
This little chunk of javascript will display 1 then 2. The anonymous function can access the o1 variable because it exists on its scope chain. However the anonymous function has an entirely independant scope in which it could create another o1 variable and thereby hide any other further down the scope chain. Note also that all variables in the entire chain remain, hence o2 would continue to exist holding an object reference for as long as the fn varialbe holds the function reference.
Now compare with C# anonymous functions:-
class C1 { public int n {get; set;} }
class C2 { public string dummy { get; set; } }
Func<int> thing() {
var o1 = new C1() {n=1};
var o2 = new C2() {dummy="Hello"};
return delegate { return o1.n++; };
}
...
Func<int> fn = thing();
Console.WriteLine(fn());
Console.WriteLine(fn());
In this case the anonymous function is not creating a truely independant scope any more than variable declaration in any other in-function { } block of code would be (used in a foreach
, if
, etc.)
Hence the same rules apply, code outside the block cannot access variables declared inside the block but you cannot reuse an identifier either.
A closure is created when the anonymous function is passed outside of the function that it was created in. The variation from the Javascript example is that only those variables actually used by the anonymous function will remain, hence in this case the object held by o2 will be available for GC as soon as thing completes,
If I remember correctly, the compiler creates a class member of the outside variables referenced in the anonymous method, in order to make this work.
Here is a workaround:
class Program
{
void Main()
{
VoidFunction t = RealFunction;
int i = 1;
}
delegate void VoidFunction();
void RealFunction() { int i = 0; }
}
It has to be that way to allow anonymous methods (and lambdas) to use local variables and parameters scoped in the containing method.
The workarounds are to either use different names for the variable, or create an ordinary method.