Uświadomiłem sobie boleśnie, jak często trzeba pisać następujący wzorzec kodu w kodzie GUI sterowanym zdarzeniami, gdzie
private void DoGUISwitch() {
// cruisin for a bruisin' through exception city
object1.Visible = true;
object2.Visible = false;
}
staje się:
private void DoGUISwitch() {
if (object1.InvokeRequired) {
object1.Invoke(new MethodInvoker(() => { DoGUISwitch(); }));
} else {
object1.Visible = true;
object2.Visible = false;
}
}
Jest to niezręczny wzór w języku C #, zarówno do zapamiętania, jak i do pisania. Czy ktoś wymyślił jakiś skrót lub konstrukcję, która do pewnego stopnia automatyzuje to? Byłoby fajnie, gdyby istniał sposób na dołączenie funkcji do obiektów, które wykonują tę kontrolę, bez konieczności przechodzenia przez całą tę dodatkową pracę, na przykład object1.InvokeIfNecessary.visible = true
skrótu typu.
Poprzednie odpowiedzi omawiały niepraktyczność za każdym razem wywoływania Invoke (), a nawet wtedy składnia Invoke () jest zarówno nieefektywna, jak i nadal niezręczna.
Czy ktoś wymyślił jakieś skróty?
c#
multithreading
winforms
thread-safety
invokerequired
Tom Corelis
źródło
źródło
object1.InvokeIfNecessary.Visible = true
linią; sprawdź moją zaktualizowaną odpowiedź i daj mi znać, co myślisz.Odpowiedzi:
Podejście Lee można jeszcze bardziej uprościć
I można tak nazwać
Nie ma potrzeby przekazywania kontroli jako parametru do delegata. C # automatycznie tworzy zamknięcie .
AKTUALIZACJA :
Według kilku innych plakatów
Control
można uogólnić jakoISynchronizeInvoke
:DonBoitnott zauważył, że w przeciwieństwie do tego
Control
doISynchronizeInvoke
interfejsu wymaga tablicy obiektów dlaInvoke
metody jako listy parametrów dla metodyaction
.AKTUALIZACJA 2
Edycje sugerowane przez Mike'a de Klerka (patrz komentarz w pierwszym fragmencie kodu wstawiania punktu):
Zobacz uwagi ToolmakerSteve poniżej, aby uzyskać obawy dotyczące tej sugestii.
źródło
ISynchronizeInvoke
zamiast tegoControl
? (Uznanie dla Jona Skeeta stackoverflow.com/questions/711408/... )ISynchronizeInvoke
. Ale jedynym typem, który z niego wywodzi (według Reflectora) jestControl
, więc przewaga jest ograniczona.while (!control.Visible) ..sleep..
. Dla mnie ma nieprzyjemny zapach kodu, ponieważ jest to potencjalnie nieograniczone opóźnienie (w niektórych przypadkach może nawet nieskończona pętla), w kodzie, który może zawierać osoby dzwoniące, które nie oczekują takiego opóźnienia (lub nawet impasu). IMHO, każde użycieSleep
powinno być obowiązkiem każdego dzwoniącego, LUB powinno być w osobnym opakowaniu, które jest wyraźnie oznaczone pod względem konsekwencji. IMHO, zwykle lepiej byłoby „mocno zawieść” (wyjątek, złapać podczas testowania) lub „nic nie robić”, jeśli kontrola nie jest gotowa. Komentarze?InvokeRequired
informuje, czy wątek wywołujący różni się od wątku, który utworzył formant.Invoke
przekazuje akcję z wątku wywołującego do wątku kontrolki, w którym jest wykonywana. Zapewnia to, na przykład, że obsługa zdarzenia kliknięcia nigdy nie jest przerywana.Możesz napisać metodę rozszerzenia:
I użyj tego w ten sposób:
EDYCJA: Jak zauważył Simpzon w komentarzach, możesz również zmienić podpis na:
źródło
Oto formularz, którego używałem w całym kodzie.
Oparłem to na wpisie na blogu tutaj . Nie miałem takiego podejścia, które mnie zawiodło, więc nie widzę powodu, aby komplikować mój kod sprawdzeniem
InvokeRequired
właściwości.Mam nadzieję że to pomoże.
źródło
InvokeRequired
jeśli kod mógł zostać wykonany przed wyświetleniem formantu lub wystąpi krytyczny wyjątek.Utwórz plik ThreadSafeInvoke.snippet, a następnie możesz po prostu wybrać instrukcje aktualizacji, kliknąć prawym przyciskiem myszy i wybrać „Surround With ...” lub Ctrl-K + S:
źródło
Oto ulepszona / połączona wersja odpowiedzi Lee, Olivera i Stephana.
Szablon pozwala na elastyczny i bezodrzutowy kod, który jest znacznie bardziej czytelny, a dedykowany delegat zapewnia wydajność.
źródło
Wolę użyć pojedynczego wystąpienia metody Deleguj zamiast tworzyć nowe wystąpienie za każdym razem. W moim przypadku wyświetlałem komunikaty o postępach i (informacje / błędy) z Backroundworker kopiującego i rzutującego duże dane z instancji SQL. Za każdym razem po około 70000 postępach i połączeniach z wiadomościami mój formularz przestał działać i wyświetlał nowe wiadomości. Nie zdarzyło się to, gdy zacząłem używać jednego delegata globalnej instancji.
źródło
Stosowanie:
Kod:
źródło
Lubię to robić trochę inaczej, lubię nazywać siebie w razie potrzeby Akcją,
jest to przydatny wzorzec, IsFormClosing to pole, które ustawiłem na True, kiedy zamykam formularz, ponieważ wciąż mogą być uruchomione wątki w tle ...
źródło
Nigdy nie powinieneś pisać kodu, który wygląda tak:
Jeśli masz taki kod, to aplikacja nie jest bezpieczna dla wątków. Oznacza to, że masz kod, który już wywołuje DoGUISwitch () z innego wątku. Jest za późno, aby sprawdzić, czy jest w innym wątku. InvokeRequire musi zostać wywołane PRZED nawiązaniem połączenia z DoGUISwitch. Nie powinieneś uzyskiwać dostępu do żadnej metody lub właściwości z innego wątku.
Odwołanie: Control.InvokeRequired Właściwość, w której można przeczytać następujące informacje:
W architekturze jednoprocesorowej nie ma problemu, ale w architekturze wieloprocesorowej możesz przypisać część wątku interfejsu użytkownika procesorowi, w którym działał kod wywołujący ... a jeśli procesor ten różni się od miejsca, w którym wątek interfejsu użytkownika działało wtedy, gdy wątek wywołujący kończy się, system Windows pomyśli, że wątek interfejsu użytkownika został zakończony i zabije proces aplikacji, tzn. aplikacja zakończy działanie bez błędu.
źródło
invoke()
et al, zanim kontrola otrzyma kontrolę, ale IMHO nie opisuje tego, co opisałeś. Cały sens tegoinvoke()
nonsensu polega na aktualizacji interfejsu użytkownika w sposób bezpieczny dla wątków, i pomyślałbym, że umieszczenie większej liczby instrukcji w kontekście blokowania doprowadziłoby do jąkania się? (Ugh ... cieszę się, że przestałem używać technologii M $. Tak skomplikowane!)