2010-06-24 14 views
14

Oto mój hipotetyczny przykład. Mam bardzo proste okno WPF z jednym przyciskiem. Zdarzenie Button.Click ma moduł obsługi, który działa w ten sposób.Dlaczego wyjątki nie są propagowane przez program rozsyłający WPF Dispatcher.Invoke?

Action doit =() => 
{ 
    Action error =() => { throw new InvalidOperationException("test"); }; 

    try { 
     this.Dispatcher.Invoke(error, DispatcherPriority.Normal); 
    } catch (Exception ex) { 
     System.Diagnostics.Trace.WriteLine(ex); 
     throw; 
    } 
}; 
doit.BeginInvoke(null, null); 

Spodziewam się, że wyjątek zostanie złapany i spisane przez wywołanie Trace.WriteLine. Zamiast tego nie zostanie przechwycony żaden wyjątek, a aplikacja zostanie zerwana.

Czy ktoś wie o możliwym wytłumaczeniu, dlaczego tak się dzieje? A jakie obejście proponujesz, aby wychwycić wyjątki zgłoszone przez delegata, na który powołuje się Dispatcher.Invoke?

Aktualizacja 1: Wpisuję kod throw w kod obsługi wyjątku. Nie chcę zignorować wyjątku. Chodzi mi o to, aby właściwie się nim posługiwać. Problem polega na tym, że kod obsługi wyjątku nigdy nie jest wykonywany.

Pamiętaj, że jest to przykład hipotetyczny. Mój prawdziwy kod nie wygląda tak. Załóżmy również, że nie mogę zmienić kodu w metodzie, która ma zostać wywołana.

Aktualizacja 2: Rozważ ten podobny przykład. Zamiast okna WPF mam okno Windows Forms. Ma przycisk z prawie dokładnie taką samą obsługą. Jedyną różnicą jest kod wywołania. Tak to wygląda.

this.Invoke(error); 

W Windows Forms wykonywany jest kod obsługi wyjątków. Dlaczego różnica?

Odpowiedz

5

AKTUALIZACJA: Aby obserwować wyjątek w innym wątku, chcesz użyć Task, kolejka go do wątku Dispatcher (używając TaskScheduler.FromCurrentSynchronizationContext) i czekać na niego, jako takich:

var ui = TaskScheduler.FromCurrentSynchronizationContext(); 
Action doit =() => 
{ 
    var error = Task.Factory.StartNew(
     () => { throw new InvalidOperationException("test"); }, 
     CancellationToken.None, 
     TaskCreationOptions.None, 
     ui); 

    try { 
     error.Wait(); 
    } catch (Exception ex) { 
     System.Diagnostics.Trace.WriteLine(ex); 
    } 
}; 
doit.BeginInvoke(null, null); 

AKTUALIZACJA (znowu): Ponieważ celem jest wielokrotnego użytku, ja polecam przeniesienie do Task opartym interfejs lub coś innego w oparciu o SynchronizationContext takich jak event-based asynchronous pattern, zamiast opierania komponent na Dispatcher lub ISynchronizeInvoke.

Dispatcher oparte na bazie komponenty działają tylko na WPF/Silverlight; Komponenty oparte na ISynchronizeInvoke działają tylko na Windows Forms. Komponenty oparte na komponentach będą działać z przezroczystymi formami WPF lub Windows i (z nieco większą ilością pracy) ASP.NET, aplikacjami konsolowymi, usługami systemu Windows itp.

Asynchroniczny wzorzec oparty na zdarzeniach jest starym zalecanym sposobem pisania SynchronizationContext oparte na składnikach; nadal jest w kodzie .NET 3.5-era. Jeśli korzystasz z platformy .NET 4, biblioteka zadań równoległych jest znacznie bardziej elastyczna, czysta i wydajniejsza. Pod numer TaskScheduler.FromCurrentSynchronizationContext znajduje się SynchronizationContext i jest to nowy sposób zapisu komponentów wielokrotnego użytku, które wymagają tego rodzaju synchronizacji.

+0

Sposób, w jaki radzę sobie z wychwyconymi wyjątkami, jest nieistotny. Mógłbym umieścić trochę logowania lub cokolwiek tam. Problem polega na tym, że kod obsługi wyjątku nigdy nie jest wykonywany. – jpbochi

+0

Dzieje się tak dlatego, że wyjątek jest zgłaszany w innym wątku - wątku 'Dispatcher'. –

+1

@Sthepen: Wiem o tym. Spodziewałem się uzyskać wyjątek TargetInvocationException lub coś podobnego. – jpbochi

Powiązane problemy