Nice programing

C #에서 foreach 루프에서 값 형식 인스턴스의 멤버를 수정할 수없는 이유는 무엇입니까?

nicepro 2020. 12. 8. 20:02
반응형

C #에서 foreach 루프에서 값 형식 인스턴스의 멤버를 수정할 수없는 이유는 무엇입니까?


값 유형은 불변이어야한다는 것을 알고 있지만 이는 규칙이 아니라 제안 일뿐입니다. 그래서 왜 이렇게 할 수 없습니까?

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

주석 처리 된 for 루프가 제대로 작동하는 동안 코드의 foreach 루프는 컴파일되지 않습니다. 오류 메시지 :

'foreach 반복 변수'이므로 'item'의 멤버를 수정할 수 없습니다.

왜 그런 겁니까?


foreach 문은 열거를 사용하고, 열거는 기본 모음을 변경할 수는 없지만 때문에 할 수 있지만, 어떤 객체 변경 에 의해 참조 컬렉션의 개체를. 이것이 가치 및 참조 유형 의미론이 작동하는 곳입니다.

참조 유형, 즉 클래스에서 저장되는 모든 컬렉션은 객체에 대한 참조입니다. 따라서 실제로 개체의 구성원을 건드리지 않으며 그 구성원에 대해 덜 신경을 쓸 수 없습니다. 개체를 변경해도 컬렉션에 영향을주지 않습니다.

반면에 값 유형은 전체 구조를 컬렉션에 저장합니다. 컬렉션을 변경하고 열거자를 무효화하지 않고는 멤버를 건드릴 수 없습니다.

또한 열거 자는 컬렉션에있는 값 의 복사본반환 합니다 . ref-type에서 이것은 아무 의미가 없습니다. 참조 사본은 동일한 참조가되며 범위를 벗어난 변경 사항을 사용하여 원하는 방식으로 참조 된 개체를 변경할 수 있습니다. 반면에 값 유형은 객체의 복사본뿐이므로 해당 복사본의 변경 사항은 전파되지 않습니다.


그것은 당신이 그것을 위반하는 것을 막을 수 없다는 의미의 제안이지만, 실제로는 "제안"보다 훨씬 더 많은 비중을 두어야합니다. 예를 들어, 여기에있는 이유 때문에.

값 유형은 참조가 아닌 변수에 실제 값을 저장합니다. 즉, 배열에 값이 있고 참조가 아닌 에 해당 값 복사본 이 있음을 의미합니다 item. 에서 값을 변경할 수있는 item경우 완전히 새로운 값이므로 배열의 값에 반영되지 않습니다. 이것이 허용되지 않는 이유입니다.

이렇게하려면 임시 변수를 사용하지 않고 인덱스별로 배열을 반복해야합니다.


구조는 값 유형입니다.
클래스는 참조 유형입니다.

ForEach구조는 IEnumerable 데이터 유형 요소 IEnumerator를 사용 합니다. 이 경우 변수는 읽기 전용이므로 수정할 수 없으며 값 유형이 있으므로 동일한 메모리를 공유하므로 포함 된 값을 수정할 수 없습니다.

C # lang 사양 섹션 8.8.4 :

반복 변수는 포함 된 명령문을 통해 확장되는 범위가있는 읽기 전용 지역 변수에 해당합니다.

이 문제를 해결하려면 구조의 클래스를 사용하십시오.

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

편집 : @CuiPengFei

var암시 적 유형 을 사용하는 경우 컴파일러가 도움을주기가 더 어렵습니다. 사용한다면 MyStruct구조의 경우 읽기 전용임을 알려줄 것입니다. 클래스의 경우 항목에 대한 참조는 읽기 전용이므로 item = null;루프 내부에 쓸 수는 없지만 변경 가능한 속성을 변경할 수 있습니다.

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

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.


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


I think the you can find the anwser below.

Foreach struct weird compile error in C#


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.

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

반응형