2012-09-23 6 views
11

immaginare un WPF code-behind Event Handler:Importanza dichiarare un gestore di eventi WPF come 'async' in C# 5

<Button Click="OnButtonClick" /> 

In C# 4 si dovrebbe dichiarare il vostro gestore come:

private void OnButtonClick(object sender, RoutedEventArgs e) { ... } 

in C# 5 si può dichiarare un async gestore

private async void OnButtonClick(object sender, RoutedEventArgs e) { ... } 

Allora, cosa sta facendo WPF con questo? Alcuni minuti di ricerca non hanno rivelato nulla.

Sembra che sia possibile eseguire gli aggiornamenti dell'interfaccia utente dopo le istruzioni await. Questo implica che l'attività sia proseguita nel thread Dispatcher?

Se l'errore Task ha generato un errore, verrà generato tramite WPF Dispatcher o solo tramite TaskScheduler?

Ci sono altri aspetti interessanti a questo che potrebbe essere bello da capire?

risposta

19

È possibile trovare il mio async/await intro utile.

Un metodo async viene riscritto dal compilatore per supportare l'operatore await. Ogni metodo async inizia in sincrono (in questo caso, sul thread dell'interfaccia utente) fino a quando non viene eseguita l'operazione await (che non è già stata completata).

Per impostazione predefinita, il contesto viene salvato e al termine dell'operazione, il resto del metodo è pianificato per l'esecuzione in tale contesto. Il "contesto" qui è SynchronizationContext.Current a meno che non sia null, nel qual caso è TaskScheduler.Current. Come ha sottolineato Drew, WPF fornisce uno DispatcherSynchronizationContext collegato al WPF Dispatcher.

Per quanto riguarda la gestione degli errori:

Quando si await un Task all'interno di un gestore di eventi WPF async void, la gestione degli errori è questa:

  • Le Task viene completata con un errore. L'eccezione è inclusa in AggregateException, come tutti gli errori Task.
  • L'operatore await vede che il Task è stato completato con un errore. Scarta l'eccezione originale e la re-getta, conservando la traccia dello stack originale.
  • Procedimento async void costruttore rileva l'eccezione che fuoriesce da un metodo async void e passa al SynchronizationContext che era attivo quando il metodo async void iniziata esecuzione (in questo caso, lo stesso contesto WPF).
  • L'eccezione viene sollevata (con la traccia dello stack originale e senza fastidioso spostamento AggregateException) su Dispatcher.

questo è piuttosto complicata, ma l'intento è quello di avere delle eccezioni sollevate dal async gestori di eventi sia praticamente la stessa come eccezioni sollevate dai gestori di eventi regolari.

+0

Grazie Stephen. Il fatto che il contesto dell'iniziatore sia salvato è ciò che mi mancava. Immagino di non poter pensare a nessun altro approccio che avrebbe senso. –

2

Una risposta parziale. Da MSDN:

Un metodo asincrono che ha un tipo di ritorno void non può essere atteso, e il chiamante di un metodo vuoto-ritorno non può intercettare eventuali eccezioni che il metodo genera.

Quindi eventuali errori sarebbero disponibili solo tramite TaskScheduler.

Inoltre, non c'è nulla di specifico XAML in corso con la registrazione del gestore eventi. Potrebbe essere stato fatto in codice:

this.button.Click += OnButtonClick; 

o addirittura come un lambda asincrona:

this.button.Click += async (s,e) => { ... }; 

quanto riguarda la sicurezza degli aggiornamenti dell'interfaccia utente dopo un await, sembra che la continuazione viene eseguito all'interno di SynchronisationContext.Current, che è impostato per thread. In WPF, questo è un DispatcherSynchronisationContext che è accoppiato al WPF Dispatcher che ha pompato l'evento in primo luogo.

Problemi correlati