是否在using语句未定义的行为中修改值类型?

Dan*_*Tao 11 c# struct specifications using mutable

这个问题真的是这个问题的一个分支,但我认为它应该得到自己的答案.

根据ECMA-334第15.13节(关于using声明,以下称为资源获取):

资源获取中声明的局部变量 是只读的,并且应包括初始化器.如果嵌入语句试图修改这些局部变量(通过赋值或发生编译时间错误++--操作员)或它们传递作为refout 参数.

这似乎解释了为什么下面的代码是非法的.

struct Mutable : IDisposable
{
    public int Field;
    public void SetField(int value) { Field = value; }
    public void Dispose() { }
}

using (var m = new Mutable())
{
    // This results in a compiler error.
    m.Field = 10;
}
Run Code Online (Sandbox Code Playgroud)

但是这个怎么样?

using (var e = new Mutable())
{
    // This is doing exactly the same thing, but it compiles and runs just fine.
    e.SetField(10);
}
Run Code Online (Sandbox Code Playgroud)

上述代码段在C#中是否未定义和/或非法?如果它是合法的,那么这段代码与上述规范的摘录之间有什么关系?如果它是非法的,为什么它有效?是否有一些允许它的微妙漏洞,或者它的作用仅仅归因于运气(因此人们不应该依赖这种看似无害的代码的功能)?

Jef*_*dge 2

我怀疑它编译和运行的原因是SetField(int)函数调用,而不是赋值或ref参数out调用。编译器(通常)无法知道是否SetField(int)会改变变量。

根据规范,这似乎完全合法。

并考虑替代方案。在 C# 编译器中,用于确定给定函数调用是否会改变值的静态分析显然成本过高。该规范旨在在所有情况下避免这种情况。

另一种选择是 C# 不允许对语句中声明的值类型变量进行任何方法调用using。这可能不是一个坏主意,因为IDisposable在结构上实现无论如何都是自找麻烦。但是,当 C# 语言首次开发时,我认为他们对以许多有趣的方式使用结构体寄予厚望(正如GetEnumerator()您最初使用的示例所示)。

  • 这听起来很合理,我倾向于同意你的观点。但问题仍然是这种行为实际上是否被定义。 (2认同)
  • 该值不会通过“++”或“--”或“ref”或“out”来更改,因此我认为它被定义为允许的。底线:不要声明可变结构。他们很困惑。 (2认同)
  • ...相比之下,“readonly”字段仅被视为类型的构造函数/初始化程序中的变量;在其他任何地方它都被视为普通值。如果您在“只读”字段中保存的结构上调用“SetValue”方法(并且您不在外部类型的构造函数/初始化程序内),那么您正在改变*该字段值的副本*。该字段本身保持不变。 (2认同)