更新:好吧,现在我已经完成了:我向微软提交了一份错误报告,因为我严重怀疑这是正确的行为。也就是说,对于这个问题,我仍然不能 100% 确定该相信什么;所以我可以看到“正确”的内容可以进行某种程度的解释。
我的感觉是微软要么接受这是一个错误,要么回应在using
语句中修改可变值类型变量构成未定义的行为。
此外,对于它的价值,我至少对这里发生的事情有一个猜测。我怀疑编译器正在为闭包生成一个类,将局部变量“提升”到该类的实例字段;并且由于它在一个using
街区内,因此它正在制作该字段readonly
。正如 LukeH 在对另一个问题的评论中指出的那样,这将阻止方法调用,例如MoveNext
修改字段本身(它们反而会影响副本)。
注意:为了便于阅读,我已经缩短了这个问题,尽管它仍然不是很短。有关完整的原始(较长)问题,请参阅编辑历史记录。
我已经通读了我认为是 ECMA-334 的相关部分,似乎无法找到这个问题的决定性答案。我将首先陈述问题,然后为感兴趣的人提供一些附加评论的链接。
问题
如果我有一个实现的可变值类型IDisposable
,我可以(1)调用一个方法来修改using
语句中局部变量的值的状态,并且代码的行为符合我的预期。然而,一旦我在语句的闭包内捕获了有问题的变量using
,(2) 对值的修改在本地范围内不再可见。
这种行为仅在变量被捕获在闭包内和using
语句内的情况下才明显;仅存在一个 ( using
) 或另一个条件 (闭包) 时并不明显。
为什么在语句的闭包内捕获可变值类型的变量会using
改变其本地行为?
下面是说明第 1 项和第 2 项的代码示例。两个示例都将使用以下演示Mutable
值类型:
struct Mutable : IDisposable
{
int _value;
public int Increment()
{
return _value++;
}
public void Dispose() { }
}
1. 在using
块中改变值类型变量
using (var x = new Mutable())
{
Console.WriteLine(x.Increment());
Console.WriteLine(x.Increment());
}
输出代码输出:
0 1
using
2. 在块内的闭包内捕获值类型变量
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());
}
上面的代码输出:
0 0
进一步的评论
已经注意到 Mono 编译器提供了我期望的行为(局部变量值的更改在using
+ 闭包情况下仍然可见)。我不清楚这种行为是否正确。
有关我对这个问题的更多想法,请参见此处。