Spójrz na następujący program:
class Test
{
List<int> myList = new List<int>();
public void TestMethod()
{
myList.Add(100);
myList.Add(50);
myList.Add(10);
ChangeList(myList);
foreach (int i in myList)
{
Console.WriteLine(i);
}
}
private void ChangeList(List<int> myList)
{
myList.Sort();
List<int> myList2 = new List<int>();
myList2.Add(3);
myList2.Add(4);
myList = myList2;
}
}
Zakładałem myList
, że minie ref
, a wynik minie
3
4
Lista jest rzeczywiście „przekazywana przez ref”, ale tylko sort
funkcja odnosi skutek. Poniższe stwierdzenie myList = myList2;
nie ma żadnego skutku.
Tak więc wynik jest w rzeczywistości:
10
50
100
Czy możesz mi pomóc wyjaśnić to zachowanie? Jeśli rzeczywiście myList
nie jest przekazywany-by-ref (jak wynika z myList = myList2
braku efektu), jak działa myList.Sort()
?
Zakładałem, że nawet to stwierdzenie nie odniesie skutku, a wynik będzie:
100
50
10
c#
list
pass-by-reference
nmdr
źródło
źródło
ChangeList
byłoby zwrócićList<int>
raczej niż być a,void
jeśli w rzeczywistości jest to tworzenie nowej listy.Odpowiedzi:
Jesteś przechodzącą odniesienie do wykazu , ale twój nie przechodzącą zmiennej listy przez odniesienie - tak podczas rozmowy
ChangeList
na wartość zmiennej (czyli odwołanie - myśleć „wskaźnik”) jest kopiowany - oraz zmiany do wartości danej parametr wewnątrzChangeList
nie jest widocznyTestMethod
.próbować:
To następnie przekazuje referencję do zmiennej lokalnej
myRef
(jak zadeklarowano wTestMethod
); teraz, jeśli zmienisz przypisanie parametru wewnątrzChangeList
, ponownie przypisujesz zmienną wewnątrzTestMethod
.źródło
ChangeList
, kopiowane jest tylko odniesienie - jest to ten sam obiekt. Jeśli zmienisz obiekt w jakiś sposób, wszystko , co ma odniesienie do tego obiektu, zobaczy zmianę.Początkowo można to przedstawić graficznie w następujący sposób:
Następnie sortowanie jest stosowane
myList.Sort();
Wreszcie, kiedy to zrobiłeś:
myList' = myList2
straciłeś jedną z referencji, ale nie oryginał, a kolekcja została posortowana.Jeśli użyjesz przez referencję (
ref
), wtedymyList'
imyList
staną się takie same (tylko jedno odwołanie).Uwaga: używam
myList'
do reprezentowania parametru, którego używaszChangeList
(ponieważ podałeś tę samą nazwę co oryginał)źródło
Oto łatwy sposób, aby to zrozumieć
Twoja lista to obiekt utworzony na stercie. Zmienna
myList
jest odniesieniem do tego obiektu.W C # nigdy nie przekazujesz obiektów, przekazujesz ich odwołania według wartości.
Kiedy uzyskujesz dostęp do obiektu listy za pośrednictwem przekazanego odniesienia
ChangeList
(na przykład podczas sortowania), oryginalna lista jest zmieniana.Przypisanie do
ChangeList
metody jest dokonywane do wartości referencji, stąd żadne zmiany nie są dokonywane na oryginalnej liście (nadal na stercie, ale nie ma już odniesienia do zmiennej metody).źródło
Ten link pomoże ci w zrozumieniu przekazywania przez odwołanie w C #. Zasadniczo, gdy obiekt typu referencyjnego jest przekazywany przez wartość do metody, tylko metody dostępne w tym obiekcie mogą modyfikować zawartość obiektu.
Na przykład metoda List.sort () zmienia zawartość listy, ale jeśli przypiszesz jakiś inny obiekt do tej samej zmiennej, przypisanie to będzie lokalne dla tej metody. Dlatego myList pozostaje niezmieniona.
Jeśli przekażemy obiekt typu referencyjnego za pomocą słowa kluczowego ref, to możemy przypisać inny obiekt do tej samej zmiennej, a to zmieni cały sam obiekt.
(Edit: to jest zaktualizowana wersja dokumentacji związanej powyżej).
źródło
C # po prostu wykonuje płytką kopię, gdy przechodzi przez wartość, chyba że dany obiekt jest wykonywany
ICloneable
(co najwyraźniejList
nie jest wykonywane przez klasę).Oznacza to, że kopiuje
List
siebie, ale odniesienia do obiektów na liście pozostają takie same; to znaczy, wskaźniki nadal odnoszą się do tych samych obiektów, co oryginałList
.Jeśli zmienisz wartości rzeczy
List
, do których odnoszą się twoje nowe odniesienia, zmieniszList
również oryginał (ponieważ odnosi się on do tych samych obiektów). Jednak wtedymyList
całkowicie zmienisz odniesienia, na noweList
, a teraz tylko oryginałList
odwołuje się do tych liczb całkowitych.Przeczytaj sekcję Przekazywanie parametrów typu odwołania w tym artykule MSDN na temat „Przekazywanie parametrów”, aby uzyskać więcej informacji.
„How do I Clone a Generic List in C #” from StackOverflow mówi o tym, jak utworzyć głęboką kopię listy.
źródło
Chociaż zgadzam się z tym, co wszyscy powiedzieli powyżej. Mam inne podejście do tego kodu. Zasadniczo przypisujesz nową listę do zmiennej lokalnej myList, a nie do zmiennej globalnej. jeśli zmienisz podpis ChangeList (List myList) na private void ChangeList () zobaczysz wynik 3, 4.
Oto moje rozumowanie ... Nawet jeśli lista jest przekazywana przez referencję, pomyśl o tym jako o przekazywaniu zmiennej wskaźnika według wartości. Kiedy wywołujesz ChangeList (myList), przekazujesz wskaźnik do (Global) myList. Teraz jest to przechowywane w (lokalnej) zmiennej myList. Więc teraz twoja (lokalna) mojaLista i (globalna) mojaLista wskazują na tę samą listę. Teraz wykonujesz sort => to działa, ponieważ (lokalna) myList odwołuje się do oryginalnej (globalnej) myList Następnie tworzysz nową listę i przypisujesz wskaźnik do tej (lokalnej) myList. Ale gdy tylko funkcja zakończy działanie, (lokalna) zmienna myList jest niszczona. HTH
źródło
Użyj
ref
słowa kluczowego.Spójrz na ostateczne odniesienie tutaj, aby zrozumieć przekazywanie parametrów.
Aby być konkretnym, spójrz na to , aby zrozumieć zachowanie kodu.
EDYCJA:
Sort
działa na tym samym odwołaniu (przekazywanym przez wartość) i dlatego wartości są uporządkowane. Jednak przypisanie nowego wystąpienia do parametru nie zadziała, ponieważ parametr jest przekazywany przez wartość, chyba że wstawiszref
.Umieszczanie
ref
pozwala zmienić wskaźnik na odniesienie do nowego wystąpieniaList
w twoim przypadku. Bez tegoref
możesz pracować nad istniejącym parametrem, ale nie możesz wskazać na coś innego.źródło
Istnieją dwie części pamięci przydzielonej dla obiektu typu referencyjnego. Jeden w stosie i jeden w stercie. Część na stosie (inaczej wskaźnik) zawiera odniesienie do części na stercie - gdzie przechowywane są rzeczywiste wartości.
Gdy słowo kluczowe ref nie jest używane, tworzona jest tylko kopia części na stosie i przekazywana do metody - referencja do tej samej części w stercie. Dlatego jeśli zmienisz coś w części sterty, te zmiany pozostaną. Jeśli zmienisz skopiowany wskaźnik - przypisując go tak, aby odnosił się do innego miejsca w stercie - nie wpłynie to na wskaźnik pochodzenia poza metodą.
źródło