Foreach struct weird compile error in C#

2020-07-22 18:55发布

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;
        }
    }
}

Any reasonable explanations for this?

The compiler returns:

Error: Cannot modify members of 'ms' because it is 'foreach iteration variable'

EDIT:

Extra question:

I just tried changing a string from MyFunction, and it doesn't actually update ms. BUT: If I go to quickwatch and assign the same value there, it does update ms. Why does this happen if it shouldn't even be compiling in the first place, shouldn't quickwatch throw an exception?

EDIT2:

Ok, quick watch also works on a copy of ms, so that's why I can edit it's value, it doesn't actually alter the contents of MyPrivateVariable.

5条回答
混吃等死
2楼-- · 2020-07-22 19:16

Struct has value type semantics. So any modification you make to the struct instance wouldn't affect the original instance. The C# compiler is trying to warn you of this.

查看更多
在下西门庆
3楼-- · 2020-07-22 19:16

C# doesn't iterate structs by reference in the "foreach (MyStruct ms...)" so ms in that context is immutable.

Replace MyStruct with a class instead.

QuickWatch can manipulate value types on the stack.

查看更多
三岁会撩人
4楼-- · 2020-07-22 19:17

this is because struct is valuetype and not a reference type. if MyStruct was a class it would have compiled without issues. check this thread for details.

查看更多
Melony?
5楼-- · 2020-07-22 19:27

You can't change what an iteration variable references: that is, you can't point the variable to a different instance (to find out why that is, see Why is The Iteration Variable in a C# foreach statement read-only?).

'Modifying' a struct (a value type) creates a new instance of the type, so the statement ms.MyBool = false is meaningless.

The call to MyFunction(ms) compiles because it operates on a copy of ms (though it still won't do what you might expect).

查看更多
【Aperson】
6楼-- · 2020-07-22 19:29

You're using them as mutable structs. Avoid doing that:

Why are mutable structs “evil”?

查看更多
登录 后发表回答