C# 中的 Foreach 结构奇怪的编译错误

问题描述 投票:0回答:5
namespace MyNamespace
{
    public struct MyStruct
    {
        public string MyString;
        public int MyInt;
        public bool MyBool;
    }

    public class MyClass
    {
        private List<MyStruct> MyPrivateVariable;

        public List<MyStruct> MyVariable
        {
            get
            {
                if (MyPrivateVariable == null)
                {
                    MyPrivateVariable = new List<MyStruct>();

                    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 迭代” 变量'

编辑:

补充问题:

我只是尝试从

MyFunction
更改字符串,但它实际上并没有更新
ms
。但是:如果我去快速观察并在那里分配相同的值,它会更新
ms
。如果一开始就不应该编译,为什么会发生这种情况,quickwatch 不应该抛出异常?

编辑2:

好吧,快速观看也适用于

ms
的副本,所以这就是为什么我可以编辑它的值,它实际上并没有改变
MyPrivateVariable
的内容。

c# generics struct foreach
5个回答
14
投票

您将它们用作可变结构。避免这样做:

为什么可变结构是“邪恶的”?


6
投票

结构体具有值类型语义。因此,您对结构体实例所做的任何修改都不会影响原始实例。 C# 编译器正试图警告您这一点。


5
投票

C# 不会在“foreach (MyStruct ms...)”中通过引用迭代结构,因此该上下文中的 ms 是不可变的。

用类替换 MyStruct。

QuickWatch 可以操作堆栈上的值类型。


1
投票

这是因为 struct 是值类型而不是引用类型。如果 MyStruct 是一个类,那么它的编译不会出现任何问题。检查this线程了解详细信息。


0
投票

您无法更改迭代变量引用的内容:也就是说,您不能将变量指向不同的实例(要了解原因,请参阅为什么 C# foreach 语句中的迭代变量是只读的? )。

“修改”结构体(值类型)会创建该类型的一个新实例,因此语句

ms.MyBool = false
毫无意义。

MyFunction(ms)
的调用可以编译,因为它对 ms
 副本
进行操作(尽管它仍然不会执行您可能期望的操作)。

© www.soinside.com 2019 - 2024. All rights reserved.