Foreach struct weird compile error in C#
Asked Answered
G

5

3
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.

Glissando answered 24/9, 2010 at 19:15 Comment(3)
What's the compiler message say?Spessartite
what's the compiler error message?Phila
What do you mean by "it works in Immediate"? It will execute, but if your goal is to update the elements in MyVariable, then it does not work.Erda
B
14

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

Why are mutable structs “evil”?

Babblement answered 24/9, 2010 at 19:18 Comment(0)
W
6

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.

Wingding answered 24/9, 2010 at 19:19 Comment(0)
N
5

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.

Noctilucent answered 24/9, 2010 at 19:19 Comment(1)
Sometimes it can be useful to wrap a mutable struct in a simple exposed-field Holder<T> class for use in certain situations, but simply replacing a mutable struct with a mutable class to facilitate mutation of things in collections is a bad idea. In general, each mutable object should have exactly one other object which encapsulates its mutable state. If one wants to copy something from one List<Holder<MyStruct>> to an existing spot in another, List2[1].Value = List1[3].Value;. To append one list item to another list, say List2.Add(new Holder<MyStruct>(List1[3])).Masculine
S
1

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.

Subtend answered 24/9, 2010 at 19:25 Comment(0)
H
0

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).

Heyer answered 24/9, 2010 at 19:26 Comment(3)
then why can I do modify them in Quick Watch? Nevermind, that also works on a copy of the struct, ugh why do structs even exist?Glissando
@Nico: Structs have a purpose, but mutable structs almost never do.Mario
@Nico: All you're doing is modifying a copy that then gets thrown away. It's essentially a no-op.Pillowcase

© 2022 - 2024 — McMap. All rights reserved.