2009-01-12 14 views
8

Me he encontrado con lo que debe ser un problema común. Cuando tengo un evento que puede estar suscrito a varias clases diferentes, una excepción lanzada por una de estas clases matará a la cadena de devolución de llamada; como no sé a priori en qué orden se realiza la devolución de llamada, esto puede provocar cambios de estado impredecibles para algunas clases y no para otras.¿Cómo evito que las excepciones dañen mi cadena de delegados?

En la Biblia (CLR via C#, estoy usando C# 2.0) hay un breve párrafo sobre cómo usar MulticastDelegate.GetInvocationList para evitar esto, pero nada más. Entonces mi pregunta es: ¿cuál es la mejor manera de lidiar con esto? ¿Tengo que usar MulticastDelegate.GetInvocationList cada vez que tengo un evento? ¿O necesito incluir todos los métodos que se pueden llamar como parte de la cadena de delegados en algún tipo de mecanismo de reversión? ¿Por qué todas estas opciones son tan complicadas en comparación con el modelo simple de evento/delegado que es tan fácil de usar en C#? ¿Y cómo puedo usarlo de la manera más simple sin terminar con un estado corrupto?

Gracias!

+0

nota respuesta a tu comentario –

Respuesta

11

Si simplemente invoca a un delegado, llamará a todos los métodos de destino en orden. Es necesario utilizar GetInvocationList si se desea ejecutar de forma individual - por ejemplo:

  • para comprobar Cancel después de cada
  • para capturar el valor de retorno de cada
  • continuar tras el fracaso de un objetivo individual

En cuanto a la mejor manera de usarlo: ¿cómo quieres que se comporte? No está claro para mí ... por ejemplo, esto podría adaptarse a un método de extensión bastante bien:

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

a continuación, puedes llamar myHandler.InvokeIgnoreErrors(this); (por ejemplo).

Otro ejemplo podría ser:

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; 
} 

que se detiene después de que el primer evento solicitudes de cancelación.

+0

Bien, esto parece un buen uso para los métodos de extensión. Desafortunadamente estoy atrapado en C# 2.0 - ¿Alguna idea de cómo podría hacerlo elegantemente aquí? ¡Gracias! –

+1

Simplemente use un método estático, entonces. Elimine el "this" y simplemente use YourUtilityClass.InvokeIgnoreErrors (myHandler, this); –

1

En lugar de cambiar la forma de invocar los eventos, creo que debería revisar los controladores de eventos. En mi opinión, los métodos de manejo de eventos siempre deben escribirse para que sean "seguros" y nunca dejar que se propaguen las excepciones. Esto es particularmente importante cuando maneja eventos en el código GUI donde el evento es invocado por un código externo, pero es un buen hábito para tener en todo momento.

+0

¿Qué quiere decir con 'propagar'? Claramente puedo necesitar el método n. ° 3 en mi cadena de delegados para informarme si se ha bloqueado, ya que puede que tenga que deshacer los cambios realizados en los métodos n. ° 1 y n. ° 2 (pero no en el método n. ° 4). –

+0

Luego está mal usando el patrón del observador, el punto de usar eventos es como un sistema de notificación débilmente acoplado. Aquí no solo los suscriptores y el editor deben conocer los detalles de implementación entre sí, sino que los suscriptores también deben conocer los detalles de la implementación de otros suscriptores. –

+0

Dicho esto, definitivamente hay casos en los que desearía usar la lista de invocación en lugar de simplemente invocar al delegado directamente como Marc describió. –

Cuestiones relacionadas