2012-07-10 9 views
10

z wielkim zaskoczony zaobserwowałem następujące zachowanie dzisiaj: Biorąc pod uwagę klasęZmienianie właściwości IEnumerator <T> .aktualnie

class Foo 
{ 
    prop int FooNumber { get; set; } 
} 

i ten kod

IEnumerable<Foo> foos = Enumerable.Range(0,3).Select(new Foo()); 

foreach (var foo in foos) 
    foo.Bar = 5; 

foreach (var foo in foos) 
    Console.Write(foo.Bar); // Writes 000 

podczas inicjalizacji foos do new List<Foo>{ new Foo(), new Foo(), new Foo() } sprawia, że ​​zapis w pętli " 555 ".

Moje pytanie: Dlaczego tak się dzieje i czy istnieje sposób na obejście tego bez użycia .ToList() (który potrzebuje komentarza, ponieważ nie jest tu potrzebny).

+4

Witaj w cudownym świecie tego, co ReSharper nazwałby "możliwym wielokrotnym wyliczeniem". Wyliczalny to ** nie ** zbiór. Fakt, że czasami wyliczany jest nad kolekcją i można ją modyfikować pod spodem, jest efektem ubocznym. –

Odpowiedz

20

Zdarza się, ponieważ foos jest dynamicznie generowany za każdym razem, gdy go wyliczysz. Tak więc podczas pierwszej iteracji ustawiasz wartości właściwości na obiektach, które nie są już przywoływane przez nic po zakończeniu iteracji. Druga iteracja działa na świeżo skonstruowanych obiektach, które mają domyślną wartość właściwości.

Inicjowanie foos do listy „trwałych” obiektów zmienia rzeczy, podobnie jak przy użyciu .ToList() z tego samego powodu (lista „stałe” jest skonstruowany i powtórzyć ponad dwukrotnie; oryginału dynamicznie produkowane IEnumerable się powtórzyć tylko na jeden raz).

Po ustaleniu, że należy użyć .ToList() tutaj: w ogóle nie czuję, że potrzebuje komentarza, ponieważ nie jest w zwyczaju iteracyjne nad dynamicznie wyprodukowane sekwencje więcej niż raz (wierzę wiele kodu narzędzia analizy ostrzec przed tym), ale za wszelką cenę pisz go.

+0

To wyjaśnia, dzięki. Byłem pod fałszywym wrażeniem, że po pierwszym wyliczeniu wynik będzie gdzieś przechowywany. – Jens

3

Wydaje się oczywiste, co się dzieje: za każdym razem, gdy się wyliczasz, tworzysz nowe obiekty Foo.

Jeśli chcesz wartości właściwości (Foo.Bar) mają być zachowane, to będziemy mieli do utrzymania Foo gdzieś i ToList() to prosty sposób to zrobić.

Powiązane problemy