In C#, why can't I modify the member of a value type instance in a foreach loop?

前端 未结 7 835
隐瞒了意图╮
隐瞒了意图╮ 2020-12-01 11:37

I know that value types should be immutable, but that\'s just a suggestion, not a rule, right? So why can\'t I do something like this:

struct MyStruct
{
             


        
相关标签:
7条回答
  • 2020-12-01 12:01

    Structures are value types.
    Classes are reference types.

    ForEach construct uses IEnumerator of IEnumerable data type elements. When that happens, then variables are read-only in the sense, that you can not modify them, and as you have value type, then you can not modify any value contained, as it shares same memory.

    C# lang spec section 8.8.4 :

    The iteration variable corresponds to a read-only local variable with a scope that extends over the embedded statement

    To fix this use class insted of structure;

    class MyStruct {
        public string Name { get; set; }
    }
    

    Edit: @CuiPengFei

    If you use var implicit type, it's harder for the compiler to help you. If you would use MyStruct it would tell you in case of structure, that it is readonly. In case of classes the reference to the item is readonly, so you can not write item = null; inside loop, but you can change it's properties, that are mutable.

    You can also use (if you like to use struct) :

            MyStruct[] array = new MyStruct[] { new MyStruct { Name = "1" }, new MyStruct { Name = "2" } };
            for (int index=0; index < array.Length; index++)
            {
                var item = array[index];
                item.Name = "3";
            }
    
    0 讨论(0)
  • 2020-12-01 12:03

    I think the you can find the anwser below.

    Foreach struct weird compile error in C#

    0 讨论(0)
  • 2020-12-01 12:04

    NOTE: As per Adam's comment, this isn't actually the correct answer/cause of the problem. It's still something worth bearing in mind though.

    From MSDN. You can't modify the values when using the Enumerator, which is essentially what foreach is doing.

    Enumerators can be used to read the data in the collection, but they cannot be used to modify the underlying collection.

    An enumerator remains valid as long as the collection remains unchanged. If changes are made to the collection, such as adding, modifying, or deleting elements, the enumerator is irrecoverably invalidated and its behavior is undefined.

    0 讨论(0)
  • 2020-12-01 12:06

    It's a suggestion in the sense that there's nothing stopping you from violating it, but it should really be afforded much more weight than "it's a suggestion". For instance, for the reasons you're seeing here.

    Value types store the actual value in a variable, not a reference. That means that you have the value in your array, and you have a copy of that value in item, not a reference. If you were allowed to change the values in item, it would not be reflected on the value in the array since it's a completely new value. This is why it isn't allowed.

    If you want to do this, you'll have to loop over the array by index and not use a temporary variable.

    0 讨论(0)
  • 2020-12-01 12:06

    Make MyStruct a class (instead of struct) and you'll be able to do that.

    0 讨论(0)
  • 2020-12-01 12:19

    Because foreach uses an enumerator, and enumerators can't change the underlying collection, but can, however, change any objects referenced by an object in the collection. This is where Value and Reference-type semantics come into play.

    On a reference type, that is, a class, all the collection is storing is a reference to an object. As such, it never actually touches any of the object's members, and couldn't care less about them. A change to the object won't touch the collection.

    On the other hand, value types store their entire structure in the collection. You can't touch its members without changing the collection and invalidating the enumerator.

    Moreover, the enumerator returns a copy of the value in the collection. In a ref-type, this means nothing. A copy of a reference will be the same reference, and you can change the referenced object in any way you want with the changes spreading out of scope. On a value-type, on the other hand, means all you get is a copy of the object, and thus any changes on said copy will never propagate.

    0 讨论(0)
提交回复
热议问题