2011-04-13 1 views
52

값 유형은 변경 가능해야한다는 것을 알고 있지만 그것은 단지 제안 일뿐 규칙이 아닙니다. 맞습니까? 루프 댓글을 달았 잘 작동하는 동안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(); 
    } 
} 

코드의 foreach 루프가 컴파일되지 않습니다 : 그래서 내가 왜 이런 일을 할 수 없습니다. 오류 메시지 :

그것이

이 왜를하는 '반복 변수의 foreach는'이기 때문에 '항목'의 구성원을 수정할 수 없습니다?

+3

+1 : 좋은 질문입니다. 오랫동안 'foreach' 루프 변수를 수정할 수 없다는 것을 알고 있었지만 실제로 배운 적이 없습니다 ** ** **! – jp2code

답변

54

는 foreach는 열거자를 사용하기 때문에, 그리고 열거는 기본 모음을 변경할 수 없지만 그러나 컬렉션 오브젝트에 의해 참조되는 오브젝트 을 변경할 수있다. 여기서 Value 및 Reference 유형의 의미가 작용합니다.

참조 유형 즉, 클래스에서 모든 컬렉션은 객체에 대한 참조입니다. 그와 같이, 그것은 실제로 어떤 객체의 멤버와도 접촉하지 않으며, 그것들에 대해서별로 관심을 가질 수 없다. 개체를 변경해도 컬렉션에 영향을주지 않습니다.

한편, 값 형식은 전체 구조를 컬렉션에 저장합니다. 컬렉션을 변경하고 열거자를 무효화하지 않고 멤버를 만질 수 없습니다.

또한 열거자는 컬렉션의 값인을 반환합니다. ref-type에서 이것은 의미가 없습니다. 참조 사본은 동일한 참조이며 변경 내용이 범위를 벗어나서 원하는 방식으로 참조 된 객체를 변경할 수 있습니다. 반면에 값 유형에서는 얻는 모든 것이 오브젝트의 사본이라는 것을 의미하므로 해당 사본의 변경 사항은 절대로 전파되지 않습니다.

+0

우수 답변. "값 유형이 컬렉션에서 전체 구조를 저장"하는 것에 대한 더 많은 증거를 어디에서 찾을 수 있습니까? – CuiPengFei

+0

@CuiPengFei : 값 유형은 전체 값을 변수에 저장합니다. 배열은 변수의 블록 일뿐입니다. –

+0

바로. 값 유형은 컬렉션, 로컬 변수 등 모든 컨테이너에 배치 된 전체 구조를 유지합니다. 덧붙여서, 이것이 구조체를 변경 불가능하게 만드는 것이 더 나은 이유입니다. "tar of the same "어떤 대상이 변화를 가져 왔는지, 그것이 전파 될지 그리고 어디에 전파 될지를 추적하는 것은 어렵습니다. 스트링에서 발생하는 전형적인 문제들입니다. – Kyte

2

참고 :아담의 의견에 따르면 실제로는 정확한 대답/문제의 원인이 아닙니다. 그것은 여전히 ​​염두에 두어야 할 가치가있는 것입니다.

MSDN. 열거자를 사용할 때 값을 수정할 수 없습니다. 본질적으로 무엇입니까 foreach이 (가) 수행 중입니다.

는 열거자는 컬렉션의 데이터를 읽을 사용할 수 있지만 은 기본 모음을 수정하는 데 사용할 수 없습니다.

열거자는 컬렉션이 변경되지 않은 한 유효한 상태로 유지됩니다. 컬렉션이 변경된 경우 요소를 추가, 수정 또는 삭제하는 등의 열거자는 으로 복구 할 수 없게 무효화되고 해당 동작은 정의되지 않습니다.

+1

그 것처럼 보일 수도 있지만 문제는 아닙니다. 'foreach'를 통해 얻은 인스턴스의 속성이나 필드 값을 확실히 수정할 수 있습니다. MSDN 기사가 말하는 것이 아닙니다. 컬렉션 자체가 아닌 컬렉션의 * member *를 수정하기 때문입니다. 여기서 문제는 값 유형 대 참조 유형 의미입니다. –

+0

실제로, 나는 당신의 대답을 읽고 내 방식으로 실수를 깨달았습니다. 그래도 여전히 유용하기 때문에 나는 이것을 떠날 것이다. – Ian

+0

고마워,하지만이게 이유라고 생각하지 않아. 컬렉션 자체가 아닌 컬렉션의 요소 멤버 중 하나를 변경하려고하기 때문에. 그리고 MyStruct를 구조체에서 클래스로 변경하면 foreach 루프가 작동합니다. – CuiPengFei

14

그것은 당신이 그것을 위반하는 것을 막을 수는 없다는 의미에서의 제안이지만 "그것은 제안"보다 훨씬 더 많은 무게를 감당해야합니다. 예를 들어, 당신이 여기서 보는 이유 때문입니다.

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

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

+0

Downvoter 관심 댓글을 게시합니까? –

+0

나는 downvote하지 않았지만, 나는 [foreach가 읽기 전용 인 이유에 대해 언급 한 이유가 완전히 정확하지는 않습니다.] (http://stackoverflow.com/questions/4004755/why-is-foreach-loop-read-only- in-c/4004778 # 4004778). –

+0

@Steven : 내가 언급 한 이유는 링크 된 답변 에서처럼 철저히 기술되지 않았다면 정확합니다. –

1

MyStruct를 구조체 대신 클래스로 만들면 그렇게 할 수 있습니다.

+0

이것은 VT (값 유형)와 RT (참조 유형)를 기반으로하지만 맞지는 않습니다. – JonH

+0

네 말이 맞아. 나는 그 요점을 놓쳤다. –

+0

고마워, 그게 효과가있을거야. 그러나 나는 그 이유를 알아 내려고 노력하고 있습니다. – CuiPengFei

1

값 유형은 called by value입니다. 즉, 변수를 평가할 때 변수의 복사본이 만들어집니다. 허용 된 경우에도 원본 인스턴스 MyStruct 대신 복사본을 편집하게됩니다.

foreach is readonly in C#. 참조 유형 (클래스)의 경우에만 참조가 읽기 전용으로이 많이 변경되지 않습니다, 그래서 당신은 여전히 ​​다음을 수행 할 수 있습니다 : 그러나 값 형식 (구조체)에 대한

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

, 전체 개체가 읽기 전용 귀하가 겪고있는 결과로 이어집니다. foreach에서는 개체를 조정할 수 없습니다.

+0

감사합니다. 그러나 귀하의 설명이 100 % 맞다고 생각하지 않습니다. MyStruct에 ChangeName 메서드를 추가하고 foreach 루프에서이 메서드를 호출하면 올바르게 작동하기 때문입니다. – CuiPengFei

+0

@CuiPengFei : 컴파일됩니다. 그러나 나중에 원래 인스턴스를 확인하면 변경되지 않았 음을 알 수 있습니다. (가치 유형에 의한 '가치에 의한 호출'때문입니다.) –

+0

아, 예, 미안한 점은 유감스럽게 생각합니다. – CuiPengFei

8

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

ForEach 구조체는 IEnumerator를 을 IEnumerable 데이터 형식 소자를 사용한다. 그런 일이 발생하면 변수는 의미 상 읽기 전용이며 변수를 수정할 수없고 값 유형을 가짐에 따라 동일한 메모리를 공유하기 때문에 포함 된 값을 수정할 수 없습니다.

C# LANG 사양 섹션 8.8.4 :

반복 해 변수

이 사용하는 클래스를 해결하기 위해 내장형 문 위에 연장 된 범위와 읽기 전용 로컬 변수에 대응

insted 구조의;

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

편집 : @CuiPengFei

당신이 var 암시 적 형식을 사용하는 경우 컴파일러는 당신을 도울 수 있도록, 그것은 어렵습니다. MyStruct을 사용하면 구조의 경우 읽기 전용임을 알립니다. 클래스의 경우 항목에 대한 참조는 읽기 전용이므로 루프 내에 item = null;을 쓸 수는 없지만 변경할 수있는 속성은 변경할 수 있습니다.

(당신이 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

감사하지만 어떻게 MyStruct에 ChangeName 메서드를 추가하고 foreach 루프에서 호출하면 사실 잘 설명 할 수 있습니까? – CuiPengFei

+0

마지막 코멘트가 잘못되었습니다. 죄송합니다. – CuiPengFei

관련 문제