为什么在 using 语句中的闭包内捕获可变结构变量会改变其本地行为? [英] Why does capturing a mutable struct variable inside a closure within a using statement change its local behavior?

查看:29
本文介绍了为什么在 using 语句中的闭包内捕获可变结构变量会改变其本地行为?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

更新:好吧,现在我已经完成了:我就此向 Microsoft 提交了错误报告,因为我严重怀疑这是正确的行为.也就是说,对于这个问题,我仍然不能 100% 确定该相信什么;所以我可以看到什么是正确的"是可以接受某些级别的解释的.

我的感觉是,微软要么接受这是一个错误,要么回应说在 using 语句中修改可变值类型变量构成未定义的行为.

此外,就其价值而言,我至少对这里发生的事情有猜测.我怀疑编译器正在为闭包生成一个类,将局部变量提升"到该类的实例字段;并且由于它在 using 块中,它使字段 readonly.正如 LukeH 在 对另一个问题的评论,这将阻止诸如 MoveNext 之类的方法调用修改字段本身(它们反而会影响副本).

<小时>

注意:为了便于阅读,我缩短了这个问题,尽管它仍然不是很短.有关完整的原始(更长)问题,请参阅编辑历史记录.

我已经通读了我认为是 ECMA-334 的相关部分,但似乎无法找到对这个问题的决定性答案.我将首先陈述问题,然后为感兴趣的人提供一些其他评论的链接.

问题

如果我有一个实现 IDisposable 的可变值类型,我可以 (1) 在 using 语句中调用一个方法来修改局部变量的值的状态,并且代码的行为符合我的预期.但是,一旦我在 using 语句中的 within 闭包中捕获了有问题的变量,(2) 对该值的修改在局部作用域中不再可见.

这种行为仅在变量被捕获在闭包内using语句中的情况下才明显;当只有一个(using)或另一个条件(闭包)存在时,它并不明显.

为什么在 using 语句中的闭包中捕获可变值类型的变量会改变其本地行为?

以下是说明第 1 项和第 2 项的代码示例.这两个示例都将使用以下演示 Mutable 值类型:

struct Mutable : IDisposable{int_value;公共整数增量(){返回_值++;}公共无效处置(){}}

1.在 using

中改变值类型变量

using (var x = new Mutable()){Console.WriteLine(x.Increment());Console.WriteLine(x.Increment());}

输出代码输出:

<预>01

2.在 using

内的闭包内捕获值类型变量

using (var x = new Mutable()){//x 在闭包内被捕获.函数闭包 = () =>x.增量();//现在 Increment 方法似乎不会影响值//局部变量x.Console.WriteLine(x.Increment());Console.WriteLine(x.Increment());}

以上代码输出:

<预>00

进一步评论

已经注意到 Mono 编译器提供了我期望的行为(对局部变量的值的更改在 using + 闭包情况下仍然可见).我不清楚这种行为是否正确.

有关我对这个问题的更多想法,请参阅 这里.

解决方案

这是一个已知错误;几年前我们发现了它.修复可能会中断,而且问题很模糊;这些是反对修复它的要点.因此,它从来没有得到足够高的优先级以实际修复它.

这几年来一直在我的潜在博客主题队列中;也许我应该把它写下来.

顺便说一句,您对解释错误的机制的猜想是完全准确的;那里很好的通灵调试.

所以,是的,已知错误,但无论如何感谢您的报告!

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.

解决方案

It's a known bug; we discovered it a couple years ago. The fix would be potentially breaking, and the problem is pretty obscure; these are points against fixing it. Therefore it has never been prioritized high enough to actually fix it.

This has been in my queue of potential blog topics for a couple years now; perhaps I ought to write it up.

And incidentally, your conjecture as to the mechanism that explains the bug is completely accurate; nice psychic debugging there.

So, yes, known bug, but thanks for the report regardless!

这篇关于为什么在 using 语句中的闭包内捕获可变结构变量会改变其本地行为?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆