버그 해결을 위한 모든 질문을 던져
+4 votes
1k views
			var d = new Dictionary<int, int>();
			d[0] = 10;
			d[1] = 20;
			d[2] = 30;
			d[3] = 40;

			foreach ( var kvp in d)
			{
				if (kvp.Value == 10)									
					d[kvp.Key] = 100;				
			}

처리되지 않은 예외: System.InvalidOperationException: 컬렉션이 수정되었습니다. 열거 작업이 실행되지 않을 수도 있습니다.

asked (11 point) , 1k views
대입 연산의 경우 key value type의 값도 변경 되야 하며 valuelist 값도 변경 되어야 할것입니다. 해당 key value type이 아예 대입 바꿔치기 되야 하기 때문에 하지 말라고 하는게 아닌지..

 

foreach 문을 돌아서 해당 kvp를 찾아 value 를 바꾸는 행위이기 때문에 하지 않는게 좋을거 같습니다.

 

keyvaluepair의 경우 struct 형 변수 입니다.
다시 읽어보니 이미 답이 있었네요. 댓글이라 따봉 못드리는군요ㅋㅋ
익셉션은 어떡케 알고 내주는거지..완전 갓언어....

4 answers

0 votes
수정이든, 삭제든, 추가든

foreach 안에서 하지마룽다!

 

그리고 한글로 예외내용이 친절하게 나오네요. "Dictionary라는 컬렉션이 수정되는 '예외'"가 발생했네요
answered (130 point)
어 왠지 질문을 위한 질문같은 이 에푸엠느낌은 뭐죠 ㅋㅋ
키를 수정, 삭제, 추가한게 아니라 이미 추가되어 있는 키의 값을 수정했는데 문제가 되는 이유가 뭔지 궁금해서 질문했습니다.
+2 votes
            foreach (var kvp in d.ToList())
            {
                if (kvp.Value == 10)
                    d[kvp.Key] = 100;
            }


 

이렇게 해야 된데여 ... 

 

answered (26 point)
답변 감사합니다, 근데 이건 왜 되고 저건 왜 안되는지 의문이네요
foreach ( var kvp in d) 
{ 
    if (kvp.Value == 10)
        d[kvp.Key] = 100; 
}

는 Dictionary 를 돌면서 Dictionary의 값을 수정한것이 되는것이고

 

foreach (var kvp in d.ToList())
{
    if (kvp.Value == 10)
        d[kvp.Key] = 100;
}

이것은 Dictionary 를 이용해 List 객체로 생성해서 돌면서 Dictionary를 수정하는거라 문제가 없습니다.

일단, 해당 예외가 뜨는 것은 값을 할당하는 행위에 일부로 예외를 throw해서 뜨는 것입니다.
즉, 질문자 님이 짜신 코드 잘못됬다기 보다, 언어 차원에서 그냥 하지말라고 하는 거죠.

질문자 님의 코드처럼 수정하는 것을 허용하면 키-값 페어의 신뢰성이 떨어지게 되는 문제가 있어, 객체 지향적 관점에서 안정성을 위해 일부로 못하게 막은 것이 아닐까 추측해 봅니다.

질문자님께서 의문을 가지신 부분이 충분히 이해가 됩니다.

컬렉션의 Key를 건드리지 않았고 가리키는 값을 변경했을 뿐이므로 이터레이션 과정에 혼란을 주지 않을 것임에도 불구하고 예외가 발생하는 것이 의아할 수 있을 것 같습니다.

하지만 작성하신 코드는 작업이 최종적으로 가져다 줄 결과와는 별개로 이터레이션 중에 사전 객체의 인덱싱 메소드를 호출하고 있습니다. d[kvp.Key] 가 그 표현식인데요. d 안에는 kvp.Key를 키로 가지는 아이템이 존재할 것임이 문맥적으로 분명하므로 이터레이션에 혼선을 주지 않을 것임이 분명하지만 언어 차원에서는 알 수 없는 일이므로 에러를 뱉는 것입니다.

그렇다면 자연스럽게 드는 의문은 어째서 "kvp.Value를 바로 변경할 수 없는가" 일 것인데 그 대답은 C#이 원래 그렇게 설계되었다라고 답해드리는게 맞는 것 같네요. 검색을 좀 해보니 msdn에 좋은 대답이 있어서 가져와 보았습니다. 링크

요약하자면 IEnumerable<T>의 범용성으로 인한 C#의 선택이었다고 말씀드릴 수 있을 것 같습니다. IEnumerable<T>의 경우 꼭 Dictionary나 List와 같이 실질적인 값을 담는 컨테이너가 없이도 생성이 가능한데요. 컨테이너 없이 순서대로 아이템을 뱉을 수 있는 구조라면 어떤 것이든 IEnumerable<T>의 인터페이스를 만족시킬 수 있습니다. yield를 이용한 IEnumerable생성이 좋은 예시이구요. 이런경우는 이터레이션 아이템의 값을 변경하는것이 무의미한 상황이겠지요.

개인적으로는 변경이 필요할 때마다 사전을 search해야하는 오버헤드가 좀 거슬리긴 하는군요.

+1 vote
저도 착각하고 있었군요.
 

key의 추가/제거로 인한 컬렉션 변경만 없으면 될거라 생각했는데

value가 class(참조되는형)로 되있지 않으면 값 수정도 안되는군용...
answered (122 point)
수정됨
따봉 드립니다
0 votes

하는 일은 같지만 이런 건 Linq로 하면 좋을 것 같아요.

 

d.Where(x => x.Value.Equals(10)).ToList().ForEach(x => d[x.Key] = 100);
answered (65 point)

버그 해결을 위해 도움을 구하고, 도움을 주세요. 우리는 그렇게 발전합니다.

throw bug 는 프로그래밍에 대한 전분야를 다룹니다. 질문,논의거리,팁,정보공유 모든 것이 가능합니다. 프로그래밍과 관련이 없는 내용은 환영받지 못합니다.

184 질문
286 answers
311 댓글
304 users