Pytanie Czy argumenty zdarzeń są przekazywane przez odniesienie lub wartość w języku C #?


Raczej proste pytanie (chyba), ale wydaje mi się, że już nie widzę odpowiedzi. Wiem, że niektóre wartości są przekazywane za pośrednictwem wartości (jak int i long), a inne są przekazywane przez odniesienie (takie jak łańcuchy), gdy przekazujesz je do funkcji.

W moim programie używam go w tle, aby GUI nie blokował się, gdy robimy długi proces w tle. Muszę przekazać dane z powrotem do wątku interfejsu użytkownika z innego pliku, więc używam do tego zdarzeń. Teraz muszę wysłać listę tablic ciągów z powrotem do wątku GUI, aby obsłużyć tam i obawiam się, jak to będzie obsługiwane. Zasadniczo w wątku roboczym mam pętlę, która zapełni listę, odeśle ją do GUI za pośrednictwem procedury obsługi zdarzeń, a następnie wyczyści ją, aby mogła wypełnić ją podczas następnego przejścia przez pętlę i zacząć od nowa.

Obawiam się, że gdy to zrobię, jeśli lista zostanie przekazana przez odniesienie, to w wątku UI myślę, że zostanie wyczyszczona w połowie odczytu, ponieważ wątek roboczy będzie nadal czyścić go w tle. Przejście w tym przypadku byłoby zdecydowanie lepsze i mogę znaleźć sposób na wymuszenie tego (skopiuj do jakiejś tablicy właściciela lub dodaj muteksa lub coś w tym rodzaju), ale pomyślałem, że byłoby dobrze wiedzieć, czy argumenty zdarzenia są przekazywane przez odwołanie lub wartość w ogóle, czy jest to to samo, co metody, i przekaże je, ponieważ zwykle przekazywane są argumenty?


12
2017-08-03 17:34


pochodzenie


To, co wiesz, jest nieprawidłowe. Ciągi i są przekazywane również według wartości. W rzeczywistości wszystko jest przekazywane przez wartość w języku C #, chyba że wyraźnie przekazujesz przez odniesienie. - Brian Rasmussen
msdn.microsoft.com/en-us/library/... spójrz na łącze do objaśnienia MSDN - MethodMan
Myślę, że miksowałem to z tablicami, które są przypisane przez referencję (tj. (Temparray = tablica1) właśnie sprawia, że ​​temparray wskazuje na tablicę1, ale żeby utworzyć nową, której potrzebuję (temparray = array1.clone ()), lub coś z tego lubić) - Xantham
@Xantham: Tablice są typami odniesienia. Więc wartość array1 jest tylko referencją. Całe zadanie kopiuje wartość jednego wyrażenia do zmiennej (lub właściwości). - Jon Skeet


Odpowiedzi:


Wiem, że niektóre wartości są przekazywane za pośrednictwem wartości (jak int i long), a inne są przekazywane przez odniesienie (takie jak łańcuchy), gdy przekazujesz je do funkcji.

Nie. Domyślnie wszystko jest przekazywane wartością - ale kiedy używasz typów referencji, "wszystko" jest referencją. To odniesienie jest przekazywane przez wartość. Jest to nie taki sam jak przekazać przez odniesienie. Widzieć mój artykuł na temat przekazywania parametrów po więcej szczegółów.

Argumenty zdarzenia są dokładnie takie same - wszelkie odwołania są przekazywane wartością, zakładając, że odpowiedni uczestnik nie używa out lub ref parametry.

EDYCJA: Aby rozwiązać problem: tak, jeśli argument zdarzenia jest zmienny i będziesz działał w innym wątku, powinieneś najpierw utworzyć kopię ... lub, alternatywnie, przekazać istniejące odniesienie, a następnie utworzyć nowe ( pusta) w swoim wątku roboczym.


20
2017-08-03 17:37



Wygląda na to, że wielu z nas jest bardzo nierozsądnie poinformowanych o terminologii. =) - Vaughan Hilts
Każdego dnia uczysz się czegoś nowego! Wcześniej sądziłem, że przekazanie parametru obiektu przez ref było zbędne. Dziękujemy za poinformowanie nas! - Rob H
Tak więc, aby upewnić się, że "referencja jest przekazywana przez wartość" i "przekazywać przez odniesienie". Czy mam rację mówiąc, że "wartość referencyjna przeszła przez wartość" oznacza, że ​​jeśli adres, na który patrzyło odniesienie, to 0x1337, zwrócę 0x1337 (adres według wartości). I tak, że "przekazanie przez odniesienie" byłoby takie, że miałbym coś, co zawsze wskazuje na odniesienie, niezależnie od tego, czy jest pod adresem 0x1337 czy gdzieś indziej? - Xantham
@Xantham: Nie sądzę, że warto mówić o adresach, szczerze mówiąc. Lepiej pokazać rzeczywisty kod i zapytaj o efekty. (Najpierw jednak przeczytaj mój artykuł i miejmy nadzieję, że wszystko wyjaśni.) - Jon Skeet


Same argumenty są domyślnie przekazywane przez wartość. Jednak w zależności od ich typu mogą to być wartości lub odniesienia do rzeczywistych wartości, z którymi pracujesz.

Zauważ, że nie jest to to samo, co powszechnie znane jako przekazywanie przez odniesienie, ponieważ skopiowana jest sama wartość faktycznie przekazana do argumentu (tj. przekazane według wartości). Jednak efekt jest podobny, ponieważ jeśli zmienisz obiekt odniesienia w metodzie, zmiany będą widoczne również poza metodą (w kodzie, w którym wywołałeś metodę).

Teraz, przechodząc przez wartość, nie ma nic szczególnego w argumentach o zdarzeniach; czy wartości są kopiowane, czy tylko ich referencje zależą całkowicie od ich typu. Więc, jak powiedziałeś, inti long argumenty (i trochę więcej, dowolne struct typy) są typami wartości, podczas gdy inne string (i dowolne wystąpienia klasy) są typami referencyjnymi.

Zwróć uwagę, że jest to prawda przekazywanie przez odniesienie jest również możliwe w C #, ale to wymaga ref słowo kluczowe.


4
2017-08-03 17:37





W standardowym wzorcu zdarzeń przekazywane są dwie referencje:

 void FormClosing(object sender, FormClosingEventArgs e) { ... }

te dwa odniesienia są przekazywane "według wartości", na przykład przy użyciu sender = null nie będzie działać poza metodą obsługi.

Ale możesz łatwo przekazać wartość:

void FormClosing(object sender, FormClosingEventArgs e)
{
    ...
    e.Cancel = true;  // this will pass back to the caller
}

3
2017-08-03 17:43





Argumenty zdarzenia są przekazywane zgodnie z typ argumentówi podpis delegata obsługi wydarzenia (w, out lub ref) - jeśli jest klasą, to przekazywana jest kopia referencji, jeśli jest to struct, wówczas przekazywana jest kopia wartości (zakładając, że podpis nie określa ani nie odsyła).

Argumenty zdarzenia są zwykle klasą (zwykle dziedziczą z EventArgs) i są często używane do zwracania dzwoniącemu takich wartości, jak eventArgs.DoCancel.


1
2017-08-03 17:38



Ponieważ jest źle. Typy referencyjne są domyślnie przekazywane przez kopię odwołania. Jest to różnica, która ma znaczenie i wydaje się, że niewielu ją rozumie. - Ed S.
@DannyVarod: Kopia odwołania jest przekazywana według wartości. To nie jest znaczenie przejść przez referencję. To ważne rozróżnienie, a Ty i OP nie udało Ci się tego dokonać. - Jon Skeet
Odpowiednia odpowiedź to "domyślnie wszystko jest przekazywane przez wartość. W przypadku typu odniesienia odniesienie jest tym, co jest przekazywane przez wartość " - Ed S.
@JonSkeet Myślałem, że to jest jasne z mojej odpowiedzi, jednak jeśli nie, to będę edytować. - Danny Varod
@DannyVarod: Zasadniczo, gdy twoja odpowiedź używa terminu "odesłanie" do domyślnego zachowania, prawdopodobnie będzie to nieprawidłowe :) - Jon Skeet