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

こ雲淡風輕ζ 提交于 2019-11-27 04:28:26

问题


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
{
    public string Name { get; set; }
}

 public class Program
{
    static void Main(string[] args)
    {
        MyStruct[] array = new MyStruct[] { new MyStruct { Name = "1" }, new MyStruct { Name = "2" } };
        foreach (var item in array)
        {
            item.Name = "3";
        }
        //for (int i = 0; i < array.Length; i++)
        //{
        //    array[i].Name = "3";
        //}

        Console.ReadLine();
    }
}

The foreach loop in the code doesn't compile while the commented for loop works fine. The error message:

Cannot modify members of 'item' because it is a 'foreach iteration variable'

Why is that?


回答1:


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.




回答2:


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.




回答3:


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



回答4:


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.




回答5:


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




回答6:


I think the you can find the anwser below.

Foreach struct weird compile error in C#




回答7:


Value types are called by value. In short, when you evaluate the variable, a copy of it is made. Even if this was allowed, you would be editing a copy, rather than the original instance of MyStruct.

foreach is readonly in C#. For reference types (class) this doesn't change much, as only the reference is readonly, so you are still allowed to do the following:

    MyClass[] array = new MyClass[] {
        new MyClass { Name = "1" },
        new MyClass { Name = "2" }
    };
    foreach ( var item in array )
    {
        item.Name = "3";
    }

For value types however (struct), the entire object is readonly, resulting in what you are experiencing. You can't adjust the object in the foreach.



来源:https://stackoverflow.com/questions/5650705/in-c-why-cant-i-modify-the-member-of-a-value-type-instance-in-a-foreach-loop

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!