Update: Well, now I've gone and done it: I filed a bug report with Microsoft about this, as I seriously doubt that it is correct behavior. That said, I'm still not 100% sure what to believe regarding this question; so I can see that what is "correct" is open to some level of interpretation.
My feeling is that either Microsoft will accept that this is a bug, or else respond that the modification of a mutable value type variable within a using
statement constitutes undefined behavior.
Also, for what it's worth, I have at least a guess as to what is happening here. I suspect that the compiler is generating a class for the closure, "lifting" the local variable to an instance field of that class; and since it is within a using
block, it's making the field readonly
. As LukeH pointed out in a comment to the other question, this would prevent method calls such as MoveNext
from modifying the field itself (they would instead affect a copy).
Note: I have shortened this question for readability, though it is still not exactly short. For the original (longer) question in its entirety, see the edit history.
I have read through what I believe are the relevant sections of the ECMA-334 and cannot seem to find a conclusive answer to this question. I will state the question first, then provide a link to some additional comments for those who are interested.
Question
If I have a mutable value type that implements IDisposable
, I can (1) call a method that modifies the state of the local variable's value within a using
statement and the code behaves as I expect. Once I capture the variable in question inside a closure within the using
statement, however, (2) modifications to the value are no longer visible in the local scope.
This behavior is only apparent in the case where the variable is captured inside the closure and within a using
statement; it is not apparent when only one (using
) or the other condition (closure) is present.
Why does capturing a variable of a mutable value type inside a closure within a using
statement change its local behavior?
Below are code examples illustrating items 1 and 2. Both examples will utilize the following demonstration Mutable
value type:
struct Mutable : IDisposable
{
int _value;
public int Increment()
{
return _value++;
}
public void Dispose() { }
}
1. Mutating a value type variable within a using
block
using (var x = new Mutable())
{
Console.WriteLine(x.Increment());
Console.WriteLine(x.Increment());
}
The output code outputs:
0
1
2. Capturing a value type variable inside a closure within a using
block
using (var x = new Mutable())
{
// x is captured inside a closure.
Func<int> closure = () => x.Increment();
// Now the Increment method does not appear to affect the value
// of local variable x.
Console.WriteLine(x.Increment());
Console.WriteLine(x.Increment());
}
The above code outputs:
0
0
Further Comments
It has been noted that the Mono compiler provides the behavior I expect (changes to the value of the local variable are still visible in the using
+ closure case). Whether this behavior is correct or not is unclear to me.
For some more of my thoughts on this issue, see here.
See Question&Answers more detail:
os