Powiedzmy, że mam zajęcia
public class MyObject
{
public int SimpleInt{get;set;}
}
I mam List<MyObject>
, a ja ToList()
to, a następnie zmieniam jedną z SimpleInt
, czy moja zmiana zostanie propagowana z powrotem do oryginalnej listy. Innymi słowy, jaki byłby wynik następującej metody?
public void RunChangeList()
{
var objs = new List<MyObject>(){new MyObject(){SimpleInt=0}};
var whatInt = ChangeToList(objs );
}
public int ChangeToList(List<MyObject> objects)
{
var objectList = objects.ToList();
objectList[0].SimpleInt=5;
return objects[0].SimpleInt;
}
Czemu?
P / S: Przepraszam, jeśli wydaje się to oczywiste. Ale teraz nie mam ze sobą kompilatora ...
.ToList()
jest to płytka kopia. Odniesienia są kopiowane, ale nowe odniesienia nadal wskazują te same wystąpienia, na które wskazują oryginalne odniesienia. Kiedy się nad tym zastanowić,ToList
nie można stworzyć żadnego,new MyObject()
kiedyMyObject
jestclass
typem.Odpowiedzi:
Tak,
ToList
utworzy nową listę, ale ponieważ w tym przypadkuMyObject
jest to typ referencyjny, nowa lista będzie zawierała odniesienia do tych samych obiektów, co oryginalna lista.Aktualizacja
SimpleInt
właściwości obiektu, do którego odwołuje się nowa lista, wpłynie również na równoważny obiekt na pierwotnej liście.(Gdyby
MyObject
została zadeklarowana jako a,struct
a nie a,class
wówczas nowa lista zawierałaby kopie elementów z pierwotnej listy, a zaktualizowanie właściwości elementu na nowej liście nie wpłynęłoby na równoważny element na pierwotnej liście).źródło
List
struktur z przypisanie takie jakobjectList[0].SimpleInt=5
nie byłoby dozwolone (błąd czasu kompilacji C #). Dzieje się tak, ponieważ wartość zwracana metody dostępu indeksatora listyget
nie jest zmienną (jest to zwrócona kopia wartości struktury), a zatem ustawienie jej elementu członkowskiego.SimpleInt
za pomocą wyrażenia przypisania jest niedozwolone (spowodowałoby to mutację kopii, która nie jest przechowywana) . Cóż, kto w ogóle używa mutowalnych struktur?foreach (var s in listOfStructs) { s.SimpleInt = 42; }
. Naprawdę paskudny haczyka jest, kiedy próbują coś takiegolistOfStructs.ForEach(s => s.SimpleInt = 42)
: kompilator pozwala go i uruchamia kod bez wyjątków, ale elemencie na liście pozostaną niezmienione!Ze źródła Reflectora:
Więc tak, Twoja oryginalna lista nie zostanie zaktualizowana (tj. Dodana lub usunięta), ale obiekty, do których istnieją odniesienia, będą.
źródło
ToList
zawsze utworzy nową listę, która nie będzie odzwierciedlać żadnych późniejszych zmian w kolekcji.Jednak będzie odzwierciedlać zmiany w samych obiektach (chyba że są to struktury zmienne).
Innymi słowy, jeśli zastąpisz obiekt z oryginalnej listy innym obiektem,
ToList
nadal będzie on zawierał pierwszy obiekt.Jeśli jednak zmodyfikujesz jeden z obiektów na oryginalnej liście,
ToList
nadal będzie zawierał ten sam (zmodyfikowany) obiekt.źródło
Przyjęta odpowiedź poprawnie odpowiada na pytanie PO na jego przykładzie. Jednak ma
ToList
to zastosowanie tylko wtedy, gdy jest stosowane do konkretnej kolekcji; nie zachowuje się, gdy elementy sekwencji źródłowej nie zostały jeszcze utworzone (z powodu odroczonego wykonania). W tym drugim przypadku możesz otrzymać nowy zestaw elementów za każdym razem, gdy dzwoniszToList
(lub wyliczasz sekwencję).Oto adaptacja kodu PO w celu zademonstrowania tego zachowania:
Chociaż powyższy kod może wydawać się wymyślony, to zachowanie może pojawić się jako subtelny błąd w innych scenariuszach. Zobacz mój inny przykład sytuacji, w której powoduje to wielokrotne pojawianie się zadań.
źródło
Tak, tworzy nową listę. Jest to zgodne z projektem.
Lista będzie zawierać te same wyniki, co oryginalna wyliczalna sekwencja, ale zmaterializowana w trwałej kolekcji (w pamięci). Pozwala to na wielokrotne wykorzystanie wyników bez ponoszenia kosztów ponownego obliczenia sekwencji.
Piękno sekwencji LINQ polega na tym, że można je komponować. Często
IEnumerable<T>
wynik jest wynikiem połączenia wielu operacji filtrowania, porządkowania i / lub projekcji. Metody rozszerzające, takie jakToList()
iToArray()
umożliwiają konwersję obliczonej sekwencji do standardowej kolekcji.źródło
Tworzona jest nowa lista, ale pozycje w niej zawarte są odniesieniami do pozycji oryginalnych (tak jak na pierwotnej liście). Zmiany w samej liście są niezależne, ale w elementach zmiany zostaną znalezione na obu listach.
źródło
Po prostu natknąłem się na ten stary post i pomyślałem o dodaniu moich dwóch centów. Generalnie, jeśli mam wątpliwości, szybko używam metody GetHashCode () na dowolnym obiekcie, aby sprawdzić tożsamość. Więc powyżej -
I odpowiedz na moim komputerze -
Więc zasadniczo obiekt, który zawiera lista, pozostaje taki sam w powyższym kodzie. Mam nadzieję, że takie podejście pomoże.
źródło
Myślę, że jest to równoważne zapytaniu, czy ToList robi głęboką lub płytką kopię. Ponieważ ToList nie ma możliwości sklonowania MyObject, musi wykonać płytką kopię, więc utworzona lista zawiera te same odwołania co oryginalna, więc kod zwraca 5.
źródło
ToList
utworzy zupełnie nową listę.Jeśli pozycje na liście są typami wartości, zostaną bezpośrednio zaktualizowane, jeśli są typami odwołań, wszelkie zmiany zostaną odzwierciedlone z powrotem w obiektach, do których istnieją odniesienia.
źródło
W przypadku, gdy obiekt źródłowy jest prawdziwym IEnumerable (tj. Nie jest tylko kolekcją spakowaną jako wyliczalna), ToList () NIE może zwrócić tych samych odwołań do obiektów, jak w oryginalnym IEnumerable. Zwróci nową listę obiektów, ale te obiekty mogą nie być takie same lub nawet równe obiektom uzyskanym przez IEnumerable po ponownym wyliczeniu
źródło
Spowoduje to również zaktualizowanie oryginalnego obiektu. Nowa lista będzie zawierała odniesienia do zawartych w niej obiektów, tak jak oryginalna lista. Możesz zmienić elementy albo, a aktualizacja zostanie odzwierciedlona w innych.
Teraz, jeśli zaktualizujesz listę (dodasz lub usuniesz element), który nie zostanie odzwierciedlony na drugiej liście.
źródło
Nie widzę nigdzie w dokumentacji, że ToList () zawsze gwarantuje zwrócenie nowej listy. Jeśli IEnumerable jest listą, skuteczniejsze może być sprawdzenie tego i po prostu zwrócenie tej samej listy.
Martwisz się, że czasami możesz chcieć mieć absolutną pewność, że zwrócona lista jest! = Do oryginalnej listy. Ponieważ Microsoft nie dokumentuje, że ToList zwróci nową Listę, nie możemy być pewni (chyba że ktoś znalazł tę dokumentację). Może się to również zmienić w przyszłości, nawet jeśli działa teraz.
new List (IEnumerable enumerablestuff) gwarantuje, że zwróci nową Listę. Zamiast tego użyłbym tego.
źródło
ToList
wydaje się tworzyć nowe odniesienie do obiektu listy, gdy wywoływane jest na aList
, BenB ma rację, mówiąc, że nie jest to gwarantowane przez dokumentację MS.IEnumerable<>.ToList()
jest faktycznie zaimplementowany jakonew List<>(source)
i nie ma określonego nadpisania dlaList<>
, więc wList<>.ToList()
rzeczywistości zwraca nowe odniesienie do obiektu listy. Ale znowu, zgodnie z dokumentacją MS, nie ma gwarancji, że to się nie zmieni w przyszłości, chociaż prawdopodobnie spowoduje to uszkodzenie większości kodu.