2012-09-23 19 views
11

imaginar un WPF de código subyacente controlador de eventos:Importancia de declarar un controlador de eventos de WPF como 'asíncrono' en C# 5

<Button Click="OnButtonClick" /> 

En C# 4 que le declare su controlador como:

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

en C# 5 se puede declarar un manejador async

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

Entonces, ¿qué está haciendo WPF con esto? Unos pocos minutos de búsqueda no dieron resultado.

Parece que es posible realizar actualizaciones de IU después de las declaraciones await. ¿Esto implica que la tarea continúa en el hilo Dispatcher?

Si el Task ha provocado un error, ¿se hubiera planteado a través del WPF Dispatcher, o solo a través del TaskScheduler?

¿Hay algún otro aspecto interesante que pueda ser agradable de entender?

Respuesta

19

Puede que mi async/await intro sea útil.

El compilador vuelve a escribir un método async para admitir el operador await. Todos los métodos async comienzan de forma síncrona (en este caso, en el hilo de la interfaz de usuario) hasta que sea await s alguna operación (que aún no se ha completado).

De forma predeterminada, el contexto se guarda y, cuando la operación finaliza, el resto del método está programado para ejecutarse en ese contexto. El "contexto" aquí es SynchronizationContext.Current a menos que sea null, en cuyo caso es TaskScheduler.Current. Como señaló Drew, WPF proporciona un DispatcherSynchronizationContext que está vinculado al WPF Dispatcher.

En cuanto a la gestión de errores:

Cuando await un Task dentro de un controlador de eventos de WPF async void, el manejo de errores es la siguiente:

  • Los Task se completa con un error. La excepción se envuelve en un AggregateException, como todos los errores Task.
  • El operador await ve que se completó el Task con un error. Desenvuelve la excepción original y la vuelve a lanzar, conservando el rastro original de la pila.
  • El constructor de métodos async void capta la excepción que escapa de un método async void y lo pasa al SynchronizationContext que estaba activo cuando el método async void comenzó a ejecutarse (en este caso, el mismo contexto WPF).
  • La excepción se plantea (con el trazado original de la pila, y sin ninguna envoltura AggregateException molesta) en el Dispatcher.

Esto es bastante complicado, pero la intención es tener excepciones planteadas desde async controladores de eventos sea prácticamente igual excepciones planteadas desde los controladores de eventos regulares.

+0

Gracias Stephen. El hecho de que el contexto del iniciador esté guardado es lo que me faltaba. Creo que realmente no puedo pensar en ningún otro enfoque que tenga sentido. –

2

Una respuesta parcial. De MSDN:

Un método asíncrono que tiene un tipo de retorno void no puede ser esperado, y la persona que llama de un método de huecos regresar no puede capturar las excepciones que el método lanza.

De modo que cualquier error solo estaría disponible a través del TaskScheduler.

Además, no hay nada específico de XAML con el registro del controlador de eventos. Se podría haber hecho en código:

this.button.Click += OnButtonClick; 

o incluso como una lambda asíncrono:

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

En cuanto a la seguridad de cambios de interfaz de usuario después de una await, parece que la continuación se ejecuta dentro SynchronisationContext.Current, que se establece por hilo. En WPF, este es un DispatcherSynchronisationContext que está acoplado al WPF Dispatcher que bombeó el evento en primer lugar.

Cuestiones relacionadas