在C#中Foreach结构奇怪的编译错误
namespace MyNamespace { public struct MyStruct { public string MyString; public int MyInt; public bool MyBool; } public class MyClass { private List MyPrivateVariable; public List MyVariable { get { if (MyPrivateVariable == null) { MyPrivateVariable = new List(); MyPrivateVariable.Add(new MyStruct()); MyPrivateVariable.Add(new MyStruct()); } return MyPrivateVariable; } } public void MyLoop() { foreach (MyStruct ms in MyVariable) { // Doesn't compile, but it works if you execute it through the Immediate window, or in Quickwatch ms.MyBool = false; // Compiles, works MyFunction(ms); } } public void MyFunction(MyStruct ms) { ms.MyBool = false; } } }
对此有任何合理的解释吗?
编译器返回:
错误:无法修改’ms’的成员,因为它是’foreach iteration variable’
编辑:
额外的问题:
我刚尝试从MyFunction
更改字符串,它实际上并没有更新ms
。 但是:如果我去快速监视并在那里分配相同的值,它会更新ms
。 如果它甚至不应该首先编译,为什么会发生这种情况,不应该快速计划抛出exception?
EDIT2:
好的,快速监视也适用于ms
的副本,这就是为什么我可以编辑它的值,它实际上并没有改变MyPrivateVariable
的内容。
你正在使用它们作为可变结构。 避免这样做:
为什么可变结构“邪恶”?
Struct具有值类型语义。 因此,对struct实例所做的任何修改都不会影响原始实例。 C#编译器试图警告你这一点。
C#不会在“foreach(MyStruct ms …)”中通过引用迭代结构,因此该上下文中的ms是不可变的。
用类替换MyStruct。
QuickWatch可以操作堆栈上的值类型。
这是因为struct是valuetype而不是引用类型。 如果MyStruct是一个类,它将编译没有问题。 查看此主题以获取详细信息
您无法更改迭代变量引用的内容:也就是说,您无法将变量指向另一个实例(要找出原因,请参阅为什么C#foreach语句中的迭代变量是只读的? )。
‘修改’结构(值类型)会创建该类型的新实例 ,因此语句ms.MyBool = false
是没有意义的。
对MyFunction(ms)
的调用编译是因为它在ms
的副本上运行(尽管它仍然不会按照您的预期执行)。