2010-05-02 4 views
10

구조의 수정이 반복자를 무효화하지 않는 데이터 구조가 C# Collections 라이브러리에 있습니까?수정이 반복자를 무효화하지 않는 C# 모음이 있습니까?

는 다음과 같은 고려 :

List<int> myList = new List<int>(); 
myList.Add(1); 
myList.Add(2); 
List<int>.Enumerator myIter = myList.GetEnumerator(); 
myIter.MoveNext(); // myIter.Current == 1 
myList.Add(3); 
myIter.MoveNext(); // throws InvalidOperationException 
+0

컬렉션을 읽는 동시에 컬렉션을 수정해야하는 이유를 설명 할 수 있습니까? 당신이 원하는 것을 할 수있는 또 다른 방법이있을 수 있습니다. –

답변

-1

사용하십시오 대신 foreach는 루프, 그리고 당신이 그것을 수정할 수 있습니다합니다. 나는 그것을 조언하지 않을 것이다. ...

+2

그래도 질문에 대답하지 않습니다. 그는 인덱스 된 컬렉션 일 필요는 없으며 반복자에 대해 묻고 있습니다. –

+0

ElementAt() 확장 메서드를 추가하면 모든 컬렉션을 인덱싱 할 수 있습니다. 나는 이것이 질문에 대답하는 것 같다고 생각한다 .... – BFree

0

아니, 그들은 존재하지 않는다. ALl C# 표준 컬렉션은 구조가 변경 될 때 분자를 무효화합니다.

var myIter = new List<int>(myList).GetEnumerator(); 
1

이 작업을 수행 할 수있는 유일한 방법은 당신이 그것을 반복하기 전에 목록의 복사본을 만드는 것입니다.

열거자는 컬렉션이 변경되지 않은 한 유효한 상태로 유지됩니다. 요소를 추가, 수정 또는 삭제하는 등 컬렉션을 변경하면 열거자는 무의미하게 무효화되고 다음에 MoveNext 또는 Reset을 호출하면 InvalidOperationException이 throw됩니다. 컬렉션이 MoveNext와 Current 사이에 수정 된 이면 Current는 열거자가 이미 무효화 된 경우에도 으로 설정된 요소를 반환합니다.

8

this MSDN article on IEnumerator에 따르면 당신이 발견 한 무효 행동을 IEnumerable의 모든 구현에 필요한 :

+6

그렇다면 그들은 그들 자신의 지침을 위반했다. 'System.Collections.Concurrent'의 모든 콜렉션들은'MoveNext'에 대한 호출 사이에서 콜렉션 수정을 허용합니다. –

+0

iterator가 컬렉션의 스냅 샷을 탐색하여 반복하는 동안 스냅 샷이 수정되지 않기 때문입니다. – naasking

+1

그게 설명서가 말하고, 왜 요소를 추가하고 제거하는 좋은 생각인지 이해하지만, 요소를 수정하면 열거자를 무효화해야하는 이유가 명확하지 않습니다. 왜 그런 방식으로 구현할 수 있는지 이해할 수 있지만 왜 그렇게해야하는지는 알 수 없습니다. – yoyo

11

예, .NET 4.0의 System.Collections.Concurrent 네임 스페이스를 살펴보십시오.

이 네임 스페이스에있는 일부 컬렉션 (예 : ConcurrentQueue<T>)의 경우이 문제는 해당 컬렉션의 "스냅 샷"에 대해 열거자를 노출시킴으로써 작동합니다. the MSDN documentation on ConcurrentQueue<T>에서

는 :

열거는 큐의 내용의 순간 인 타임 스냅 샷을 나타냅니다. 은 GetEnumerator가 호출 된 후 컬렉션에 대한 모든 업데이트를 반영하지 않습니다. 열거자는 큐에서 읽고 쓰는 동시에 동시에 사용하는 것이 안전합니다.

그러나 모든 컬렉션에는 해당되지 않습니다. 예를 들어 ConcurrentDictionary<TKey, TValue>MoveNext을 호출하는 동안 기본 컬렉션에 대한 업데이트를 유지 관리하는 열거자를 제공합니다.

the MSDN documentation on ConcurrentDictionary<TKey, TValue>에서 :

이 열거는 사전에서 반환하는 것은 그러나 그것은 이 의 순간 인 타임 스냅 샷을 표현하지 않는, 읽고 사전에 기록과 동시에 를 사용하는 것이 안전합니다 사전. 열거자를 통해 에 노출 된 내용은 GetEnumerator가 호출 된 후 사전에 대한 수정 내용을 포함 할 수 있습니다.당신이 4.0이없는 경우

후 나는 다른 사람이 맞다 및 .NET에 의해 제공되는 그러한 컬렉션이 없다 생각합니다. 그러나 항상 동일한 것을 수행하여 자신 만의 빌드를 만들 수 있습니다 ConcurrentQueue<T> (스냅 샷을 반복).

+0

완전한 대답, 감사합니다! ConcurrentQueue와 ConcurrentStack이 스냅 샷을 얻는 방법을 아십니까? 그들은 전체 컬렉션을 새로운 객체에 복사하고 사본을 열거합니까? 아니면 스냅 샷을 찍는 다른 현명한 방법이 있습니까? – tytyryty

5

이 동작을 지원하려면 꽤 복잡한 내부 처리가 필요하므로 대부분의 컬렉션에서 지원하지 않습니다 (Concurrent 네임 스페이스에 대해 확실하지 않습니다).

그러나 불변의 컬렉션을 사용하여이 동작을 매우 잘 시뮬레이션 할 수 있습니다. 그들은 을 디자인 변경하여 컬렉션을 수정할 수는 없지만 약간 다른 방식으로 처리 할 수 ​​있습니다. 이러한 종류의 처리는 복잡한 처리 없이도 열거자를 동시에 사용할 수 있습니다 (Concurrent 컬렉션에 구현 됨).

쉽게 그런 컬렉션을 구현할 수있다, 또는 당신이 (비록 .NET 4.0이 아닌 표준 부품) FSharp.Core.dll에서 FSharpList<T>를 사용할 수 있습니다

open Microsoft.FSharp.Collections; 

// Create immutable list from other collection 
var list = ListModule.OfSeq(anyCollection); 
// now we can use `GetEnumerable` 
var en = list.GetEnumerable(); 

// To modify the collection, you create a new collection that adds 
// element to the front (without actually copying everything) 
var added = new FSharpList<int>(42, list); 

불변의 컬렉션의 장점은 작업 할 수 있다는 것입니다 원래 복사본에 영향을 미치지 않으면 서 복사본을 만들 수 있으므로 원하는 동작은 "무료"입니다. 자세한 내용은 great series by Eric Lippert입니다.

관련 문제