2010-11-15 13 views
9

Deklarowałem obiekt typu Dictionary i próbowałem dodać do niego niektóre elementy. Ale nie mogę zmienić nawet wartości przedmiotu. Jest uzasadnione, że klucz nie powinien być modyfikowalny, ale dlaczego nie wartość? Dzięki.Dlaczego słownik .NET <TKey, TValue> jest niezmienny?

Dictionary<string, string> dict = new Dictionary<string, string>(); 
    dict.Add("1", "bob"); 
    dict.Add("2", "jack"); 
    dict.Add("3", "wtf"); 
    foreach (string key in dict.Keys) 
    { 
     dict[key] = "changed"; //System.InvalidOperationException: Collection was modified 
    } 

Odpowiedz

14

Sam słownik nie jest niezmienny. Ale nie można zmienić słownika podczas wyliczania go w pętli foreach.

Tutaj link do dalszego czytania: Why is The Iteration Variable in a C# foreach statement read-only?

+2

Innymi słowy, kolekcja obecnie iterowana jest niezmienna – abatishchev

+1

@abatishchev, istnieje różnica między obiektem, który nie powinien być modyfikowany, a obiektem, który nie może być modyfikowany. Termin _immutable_ odnosi się do tego ostatniego, podczas gdy zbiór, który jest iterowany, jest pierwszym. –

17

Nie wolno modyfikować kolekcji jesteś iteracji nad w foreach pętli. Nie ma to nic wspólnego ze słownikiem - dict[key] = "value"; działa idealnie dobrze poza pętlami foreach.

2

Dictionary jest nie niezmienna (pierwsza, gdyby było, to nie byłoby w stanie Add do niego).

Problem polega na tym, że próbujesz zmodyfikować kolekcję podczas iteracji nad nią - nie można zmienić kolekcji w obrębie bloku foreach za jej pomocą.

Będą działać wartości zmieniane poza foreach.

+0

Nie można uzyskać dostępu do słownika za pomocą indeksu 'int', chyba że w rzeczywistości jest to' Dictionary '. – LukeH

+0

@LukeH - całkiem dobrze. Myślał o innych kolekcjach ... – Oded

4

To nie jest niezmienna, można go zmienić, to jest tak, że obecny iterator staje się nieważny, jeśli zmodyfikować kolekcję. można „rozwiązać” ten zmuszając iterator do ukończenia przed zmodyfikować kolekcję

foreach (string key in new List<string>(dict.Keys)) 
{ 
    dict[key] = "changed"; //System.InvalidOperationException: Collection was modified 
} 
1

Próbujesz zmodyfikować kolekcję jednocześnie jesteś iteracji na nim. To nie jest dozwolone. Wyjątek jest generowany przez ruch następnej metody modułu wyliczającego w pętli foreach.

1

Jak wspomniałem @Femaref, nie można zmienić słownika podczas iteracji wewnątrz pętli foreach. Kolejna praca wokół jest użycie pętli i użyć następującego kodu, aby go zmienić

for (int i = 0; i < dict.Count; i++) 
{ 
    var key = dict.Keys.ElementAt(i); 
    dict[key] = "changed"; 
} 

Uwaga: Musisz użyć LINQ do tego.

+0

Nieefektywne dla dużych słowników, ale prawdopodobnie możliwe do zastosowania. –

+0

Kreacjoniści są nieuporządkowane. W niektórych przypadkach może się to nie udać. – SLaks

+0

jako takie .... ??? Ta logika jest niezależna od kolejności elementów w słowniku. –

2

Analogia, o której mogę pomyśleć, to próba przecięcia gałęzi drzewa podczas siedzenia na niej. Więc uważany jest za niebezpieczny.

Alternatywą może być użycie pętli for lub klonowanie listy.

1

Konwertuj dict.Keys na tablicę.

Następnie wykonaj to dla pętli.

Jeśli zmodyfikujesz kolekcję, wyrzuci wyjątek.

7

Użyto błędnego określenia "immutable". Jest powszechnie stosowany w przypadku obiektów, których stan wewnętrzny nie ulegnie zmianie po ich zainicjowaniu. Model Dictionary<TKey, TValue> można dowolnie zmieniać. To, że wyrzuca wyjątki, gdy ktoś próbuje je zmodyfikować podczas wyliczania, jest wyraźnie zaimplementowanym zachowaniem.

Prawdopodobnie nie masz pewności, dlaczego wyliczenie zmieni się, gdy zmienisz tylko wartość. Nie obejmuje to jeszcze innych odpowiedzi.

Cóż, kiedy robisz

dict[key] = "something"; 

wyliczenie będzie zmiana jeśli klucz robi nie jeszcze istnieje, ponieważ zostanie on dodany w tym przypadku, powodując wyliczenie zmienić. Z pewnością implementacja słownika może to najpierw sprawdzić i pozwolić na modyfikację, jeśli klucz już istnieje, ale domyślam się, że postępuje zgodnie z zasadą Błąd wcześnie, często kończy się niepowodzeniem, aby poprawić ogólną integralność aplikacji.

1

Firma Microsoft zdecydowała, że ​​obiekty wielokrotnego użytku, które obsługują metodę resetowania, powinny zwracać dokładnie te same dane, jeśli wykonano wiele przejść przez nie. Jest to przydatna gwarancja, jeśli chce się np. wytworzyć ciąg zawierający wszystkie elementy (pierwsze przejście może zmaksymalizować długość wszystkich elementów, a drugie przejście może je złączyć). Ponieważ nawet zmiana wartości słownika może naruszyć tę gwarancję, jest ona zabroniona. Byłoby miło, gdyby zmiany w wartości słownika powodowały tylko skrzek, jeśli faktycznie próbujemy drugiego przejścia przez kolekcję, ale to nie jest sposób, w jaki Microsoft zaprojektował rzeczy. Ponadto nie jestem zaznajomiony z wewnętrzną implementacją słownika, ale możliwe jest, że obsługuje zmianę wartości jako usunięcie i wstawienie. Jeśli to zrobi, to z pewnością złamie moduł wyliczający.

Powiązane problemy