2009-01-12 10 views
8

Natknąłem się na to, co musi być powszechnym problemem. Kiedy mam wydarzenie, które może być subskrybowane przez kilka różnych klas, wyjątek wygenerowany przez jedną z tych klas zabije łańcuch wywołań zwrotnych; ponieważ nie wiem a priori w jakiej kolejności wykonywane jest wywołanie zwrotne, może to spowodować nieprzewidywalne zmiany stanu dla niektórych klas, a nie dla innych.Jak zatrzymać wyjątki, niszcząc mój łańcuch delegatów?

W Biblii (CLR via C#, używam C# 2.0) jest krótki akapit o użyciu MulticastDelegate.GetInvocationList, aby ominąć to, ale nic więcej. Moje pytanie brzmi: jaki jest najlepszy sposób, aby sobie z tym poradzić? Czy muszę używać MulticastDelegate.GetInvocationList za każdym razem, gdy mam jakieś wydarzenie? Czy muszę załączyć wszystkie metody, które można nazwać jako część łańcucha delegatów w ramach mechanizmu wycofywania zmian? Dlaczego wszystkie te opcje są tak skomplikowane w porównaniu do prostego modelu zdarzeń/delegatów, który jest tak łatwy w użyciu w języku C#? I jak mogę użyć tego prostego sposobu, nie kończąc z uszkodzonym stanem?

Dzięki!

+0

notka Odpowiedź na Twój komentarz –

Odpowiedz

11

Jeśli po prostu wywołasz delegata, wywoła on wszystkie docelowe metody w kolejności. Trzeba użyć GetInvocationList jeśli chcesz wykonać je indywidualnie - na przykład:

  • sprawdzić Cancel po każdym
  • uchwycić wartość zwracaną każdego
  • kontynuować po niepowodzeniu indywidualnego celu

Co do najlepszego sposobu użycia: jak chcesz, aby się zachowywał? To nie jest dla mnie jasne ... na przykład, to może dopasować metodę rozszerzenia całkiem dobrze:

static void InvokeIgnoreErrors(this EventHandler handler, 
     object sender) { 
    if(handler != null) { 
     foreach(EventHandler subHandler in handler.GetInvocationList()) { 
      subHandler(sender, EventArgs.Empty); 
     } 
    } 
} 

Następnie można po prostu zadzwonić myHandler.InvokeIgnoreErrors(this); (na przykład).

Innym przykładem może być:

static bool InvokeCheckCancel(this CancelEventHandler handler, 
     object sender) { 
    if(handler != null) { 
     CancelEventArgs args = new CancelEventArgs(false); 
     foreach(CancelEventHandler subHandler in handler.GetInvocationList()) { 
      subHandler(sender, args); 
      if(args.Cancel) return true; 
     } 
    } 
    return false; 
} 

który zatrzymuje się po pierwszym przypadku prosi odwołanie.

+0

OK, wydaje się, że jest to użyteczne dla metod rozszerzenia. Niestety utknąłem w C# 2.0 - jakieś pomysły, jak mogłem to zrobić tutaj elegancko? Dzięki! –

+1

Po prostu użyj metody statycznej. Usuń "to" i po prostu użyj YourUtilityClass.InvokeIgnoreErrors (myHandler, this); –

1

Zamiast zmieniać sposób wywoływania zdarzeń, myślę, że powinieneś przejrzeć swoje programy obsługi zdarzeń. Moim zdaniem metody obsługi zdarzeń powinny zawsze być napisane tak, aby były "bezpieczne" i nigdy nie pozwalają propagować wyjątków. Jest to szczególnie ważne w przypadku obsługi zdarzeń w kodzie GUI, w którym zdarzenie jest wywoływane przez zewnętrzny kod, ale jest to dobry nawyk w każdym momencie.

+0

Co masz na myśli mówiąc "propagować"? Oczywiście mogę potrzebować metody nr 3 w moim łańcuchu delegatów, aby poinformować mnie, czy to się zawiesiło, ponieważ może będę musiał wycofać wszelkie zmiany przeprowadzone metodami # 1 i # 2 (ale nie metodą # 4). –

+0

Następnie nadużywasz wzorca obserwatora, punktem wykorzystania zdarzeń jest luźno powiązany system powiadomień. Tutaj nie tylko abonenci i wydawcy muszą znać szczegóły dotyczące implementacji, ale także znać szczegóły implementacji dotyczące innych subskrybentów. –

+0

To powiedziawszy, są zdecydowanie przypadki, w których chciałbyś użyć listy wywołań, a nie tylko wywoływać delegata bezpośrednio, jak opisał Marc. –