Outer variables
Any local variable, value parameter, or parameter array whose scope includes the lambda-expression or anonymous-method-expression is called an outer variable of the anonymous function. In an instance function member of a class, the this value is considered a value parameter and is an outer variable of any anonymous function contained within the function member. 7.15.5.1 Captured outer variables When an outer variable is referenced by an anonymous function, the outer variable is said to have been captured by the anonymous function. Ordinarily, the lifetime of a local variable is limited to execution of the block or statement with which it is associated (§5.1.7). However, the lifetime of a captured outer variable is extended at least until the delegate or expression tree created from the anonymous function becomes eligible for garbage collection. In the example using System; delegate int D(); class Test static void Main() { the local variable x is captured by the anonymous function, and the lifetime of x is extended at least until the delegate returned from F becomes eligible for garbage collection (which doesn’t happen until the very end of the program). Since each invocation of the anonymous function operates on the same instance of x, the output of the example is: 1 When a local variable or a value parameter is captured by an anonymous function, the local variable or parameter is no longer considered to be a fixed variable (§18.3), but is instead considered to be a moveable variable. Thus any unsafe code that takes the address of a captured outer variable must first use the fixed statement to fix the variable. Note that unlike an uncaptured variable, a captured local variable can be simultaneously exposed to multiple threads of execution. 7.15.5.2 Instantiation of local variables A local variable is considered to be instantiated when execution enters the scope of the variable. For example, when the following method is invoked, the local variable x is instantiated and initialized three times—once for each iteration of the loop. static void F() { However, moving the declaration of x outside the loop results in a single instantiation of x: static void F() { When not captured, there is no way to observe exactly how often a local variable is instantiated—because the lifetimes of the instantiations are disjoint, it is possible for each instantation to simply use the same storage location. However, when an anonymous function captures a local variable, the effects of instantiation become apparent. The example using System; delegate void D(); class Test static void Main() { produces the output: 1 However, when the declaration of x is moved outside the loop: static D[] F() { the output is: 5 If a for-loop declares an iteration variable, that variable itself is considered to be declared outside of the loop. Thus, if the example is changed to capture the iteration variable itself: static D[] F() { only one instance of the iteration variable is captured, which produces the output: 3 It is possible for anonymous function delegates to share some captured variables yet have separate instances of others. For example, if F is changed to static D[] F() { the three delegates capture the same instance of x but separate instances of y, and the output is: 1 1 Separate anonymous functions can capture the same instance of an outer variable. In the example: using System; delegate void Setter(int value); delegate int Getter(); class Test the two anonymous functions capture the same instance of the local variable x, and they can thus “communicate” through that variable. The output of the example is: 5
|