2012-05-24 19 views
6

Recientemente aprendí sobre el uso de los métodos de extensión C# para hacer que llamar eventos sea más fácil y los he estado usando cada vez más. Hace poco toqué un problema extraño que no entiendo, y me preguntaba si alguien podría explicarlo.Manejo de eventos con C# Extension Methods

El problema se produce al intentar establecer un método de extensión eventhandler como un controlador de eventos de otro evento. Aquí está un ejemplo de lo que estoy haciendo:

public static class EventHandlerExtensions 
{ 
    public static void Raise<TEventArgs>(
     this EventHandler<TEventArgs> eventHandler, 
     object sender, TEventArgs args) where TEventArgs:EventArgs 
    { 
     if (eventHandler != null) 
     { 
      eventHandler(sender, args); 
     } 
    } 
} 

public class Test 
{ 
    private event EventHandler<EventArgs> EventA; 
    private event EventHandler<EventArgs> EventB; 

    public Test() 
    { 
     Console.WriteLine("::Start"); 
     EventB += EventA.Raise; 
     EventA += (s, a) => Console.WriteLine("Event A raised"); 
     EventB.Raise(this, EventArgs.Empty); 
     Console.WriteLine("::End"); 
    } 
} 

En este ejemplo, Eventa debe activarse como consecuencia de EventB siendo activado. Sin embargo, cuando ejecuto este código, el evento B se dispara, pero el método de extensión en A no encuentra ningún oyente para él.

Si cambio el orden alrededor, todo funciona bien:

Console.WriteLine("::Start"); 
EventA += (s, a) => Console.WriteLine("Event A raised"); 
EventB += EventA.Raise; 
EventB.Raise(this, EventArgs.Empty); 
Console.WriteLine("::End"); 

También, llamando desde un EventA.Raise lambda funciona bien:

Console.WriteLine("::Start"); 
EventB += (s, a) => EventA.Raise(s, a); 
EventA += (s, a) => Console.WriteLine("Event A raised"); 
EventB.Raise(this, EventArgs.Empty); 
Console.WriteLine("::End"); 

Esto es sólo un ejemplo sencillo, pero Intento crear una clase que pueda volver a enviar los eventos de las fuentes de eventos agregados de la manera más limpia posible. No quiero crear métodos con nombre solo para volver a enviar los mismos eventos, y prefiero no almacenar listas de funciones de lambda que pueda desenganchar de los controladores de eventos más adelante. En su mayoría, solo tengo curiosidad de por qué está sucediendo esto?

¿Alguna idea?

+0

Eso podría ser realmente malo si tienes muchos manejadores de eventos a los que te suscribes. Buena pregunta, aunque !!! :) – IAbstract

+0

Cuando escribo el código en VS2010, al escribir 'EventA.R' justo después de' Console.WriteLine ("Start"); 'no se muestra el método de extensión en intelllisense. Lo hace si lo escribo justo antes de 'Console.WriteLine (" End ");'. Extraño. – Guillaume

Respuesta

4

Captura el valor antiguo de EventA en el cierre mediante su función Raise. Como más tarde usa + = cambia el valor de EventA, pero su cierre todavía tiene un valor antiguo.

Usted código:

EventB += EventA.Raise; 
EventA += (s, a) => Console.WriteLine("Event A raised"); 

se puede expandir en código equivalente que hace que sea claro por qué uno se hace viejo delegado:

var oldEventA = EventA; 
EventB += oldEventA.Raise; // captures old value here 
// now EventA changed to new value 
EventA = oldEventA + ((s, a) => Console.WriteLine("Event A raised");) 

Puedes añadir siguiente para antes EventB += EventA.Raise para verificar que el código realidad plantea evento antiguo para A:

EventA += (s, a) => Console.WriteLine("Old Event A raised"); 
2

Delegar objetos son inmutables. Al igual que las cuerdas. Entonces, cuando asigna EventA, crea un objeto nuevo. EventB todavía se está centrando en el anterior, el que aún no tenía ningún controlador de eventos asignado. Tienes que cambiar las dos declaraciones para solucionar el problema.