2009-03-08 25 views
20

Estoy buscando pasar un evento a una función de ayuda. Esta función adjuntará un método al evento. Sin embargo, estoy teniendo problemas para pasar el evento correctamente. He intentado pasar un EventHandler<TEventArgs>. Se compila, pero los eventos no están adjuntos (pero todavía se agregan, parece que se hace una copia del controlador de eventos).¿Cómo puedo pasar un evento a una función en C#?

Por ejemplo, si tengo esto:

public event EventHandler<EventArgs> MyEvent; 

Y la función auxiliar:

public static void MyHelperFunction<TEventArgs>(EventHandler<TEventArgs> eventToAttachTo) 
{ 
    eventToAttachTo += (sender, e) => { Console.WriteLine("Hello world"); }; 
} 

Y la persona que llama:

MyHelperFunction(MyEvent); 
MyEvent(null, new EventArgs()); // Does nothing. 
+0

@Strager: ¿Te importaría elaborar un poco sobre los detalles de cómo estabas usando esto? Encontré esta pregunta muy interesante, pero estoy teniendo dificultades para ver el caso de uso. –

+0

@John Feminella, estaba creando algunas funciones auxiliares, y una sincrónicamente espera un evento. Se utilizan principalmente para reducir la reutilización de código para varios métodos WaitFor (por ejemplo, WaitForConnected) en mis clases de red (que funcionan de forma asíncrona). – strager

Respuesta

17

La razón por la que esto no funciona es + = cuando se aplica a un delegado crea un nuevo delegado que es la combinación de lo viejo y lo nuevo. No modifica el delegado existente.

Con el fin de conseguir que esto funcione, tendrá que pasar el delegado por referencia.

public static void Helper(ref EventHandler<EventArgs> e) 
{ 
    e+= (x,y) => {}; 
} 

La razón por la que esto funciona fuera del método es porque el LHS sigue siendo el campo real. Entonces + = creará un nuevo delegado y lo asignará nuevamente al campo de miembro.

+0

Interesante. Así que supongo que + = recrea el delegado (como e = e + func). ¡Gracias por tu ayuda! Pasé varias horas intentando depurar mi código (culpándolo de enhebrar) cuando de hecho un evento no se disparaba cuando debería. – strager

+0

@strager, Cuando llegas a situaciones como esta, es una buena idea sacar el reflector. Desenrollará algunas construcciones de sintaxis engañosas y le mostrará lo que está sucediendo realmente. – JaredPar

+4

Un problema con el uso de ref: Obtengo el siguiente error si uso Helper (ref myClassInstance.MyEvent): El evento MyEvent solo puede aparecer en el lado izquierdo de + = o - = (excepto cuando se usa dentro del tipo MyClass) . ¿Cómo puedo solucionar esto? – strager

2

sólo una suposición: ¿Ha intentado pasándolo como ref?

public static void MyHelperFunction<TEventArgs>(ref EventHandler<TEventArgs> eventToAttachTo) 

MyHelperFunction(ref MyEvent); 
1

tengo una solución en la que tengo un dos interfaces. La primera interfaz tiene métodos para vincular ciertos eventos, mientras que la otra interfaz tiene métodos de eventos que pueden vincularse a esos eventos.

métodos obligar a la primera de interfaz toma la segunda interfaz como parámetro, lo que hace posible unir los eventos a los métodos de evento de cualquier clase que implementa la segunda interfaz.

¿Eso es comprensible, o prefiere algo de código? :)

+1

Código, por favor ... –

2

No es exactamente agradable, pero se puede utilizar la reflexión para hacer esto.

public EventMonitor(object eventObject, string eventName) 
    { 
     _eventObject = eventObject; 
     _waitEvent = eventObject.GetType().GetEvent(eventName); 

     _handler = new EventHandler(SetEvent); 
     _waitEvent.AddEventHandler(eventObject, _handler); 
    } 

Donde eventObject es el objeto que contiene el evento y eventName es el nombre del evento. SetEvent es su controlador de eventos.

que también tienen un método dispose de esta manera:

public void Dispose() 
    { 
     _waitEvent.RemoveEventHandler(_eventObject, _handler); 
    } 
+0

¿Ha probado que funcione? – Kairan

+0

@Kairan - Me gana. Fue hace más de 3 1/2 años. – Ray

1

Como muchos han señalado, que pasa un evento a un método o bien no es posible o no es sencilla.

  1. Por favor, aclarar, pero sospecho que su uso previsto se verá algo como:

    void Register() 
    { 
        var super = new SuperHandler(); 
    
        //not valid syntax: 
        super.HandleEvent(MyEvent1); 
        super.HandleEvent(MyEvent2); 
        super.HandleEvent(MyEvent3); 
        super.HandleEvent(MyEvent4); 
    } 
    

    Esto se puede hacer simplemente haciendo que sus controladores de eventos genéricos destinados públicamente accesible (o internamente, si lo desea) :

    public static class GenericHandler 
    { 
        public static void HandleAnyEvent(object sender, EventArgs e) 
        { 
         //handle 
        } 
    } 
    
    public class SomeClass 
    { 
        void RegisterEvents() 
        { 
         var r = new EventRaiser(); 
    
         r.ImportantThingHappened += GenericHandler.HandleAnyEvent; 
        } 
    } 
    

    en este ejemplo, mi cajón de sastre controlador está en una clase estática, pero se puede utilizar del mismo modo que una clase no estática. Además, veo que en su ejemplo ha hecho que el método sea genérico (TEventArgs). Debido a que todos los derivados de EventHandler (como CancelEventHandler) coinciden con el EventHandler base, no es necesario que involucre genéricos (ni sería útil).

  2. Si la lógica de registro es compleja o debe mantener el manejador de eventos en privado, considere usar Eventos de interfaz. Es posible que esto no cumpla su objetivo previsto de reducir la cantidad de código, pero le permitirá crear una clase que pueda manejar de forma predecible todos los eventos de un tipo específico.

    interface IRaiseEvents 
    { 
        event EventHandler ConnectionCreated; 
        event EventHandler ConnectionLost; 
    } 
    
    public class SuperHandler 
    { 
        void RegisterEvents(IRaiseEvents raiser) 
        { 
         raiser.ConnectionCreated += (sender, args) => Console.WriteLine("Connected."); 
         raiser.ConnectionLost += (sender, args) => Console.WriteLine("Disconnected."); 
        } 
    } 
    
3

acaba de llegar con este pequeño ayudante. Si se trata del evento creado por ti mismo, podrías usar un contenedor como este. Puede usar sus operadores + = para adjuntar controladores normalmente, pero puede pasar el contenedor e incluso plantear el evento desde otro lugar.

public class GenericEvent<T> where T:EventArgs 
{ 
    public event EventHandler<T> Source = delegate { }; 

    public void Raise(object sender, T arg = default(T)) 
    { 
     Source(sender, arg); 
    } 

    public void Raise(T arg = default(T)) 
    { 
     Source(this, arg); 
    } 

    public void AddHandler(EventHandler<T> handler) 
    { 
     Source += handler; 
    } 

    public void RemoveHandler(EventHandler<T> handler) 
    { 
     Source -= handler; 
    } 

    public static GenericEvent<T> operator +(GenericEvent<T> genericEvent, EventHandler<T> handler) 
    { 
     genericEvent.AddHandler(handler); 
     return genericEvent; 
    } 
} 

Crear el evento como:

public GenericEvent<EventArgs> MyEvent = new GenericEvent<EventArgs>(); 

asociar controladores: eventos

MyEvent += (s,e) => {}; 

Levante:

MyEvent.Raise(); 
+1

Una buena clase simple. Para hacerlo más complejo, sugeriría usar una lista de eventos suscritos. Desuscribirá todos los eventos suscritos, si el objeto GenericEvent está dispuesto. Esto podría significa que GenericEvent implementa la interfaz IDisposable. –

1

paso algo parecido Acción e = e => MyEvent + = e; ¿Y llamar desde el método con el controlador? Tiene la ventaja de trabajar con clases .NET.

+0

Tienes razón. Eso es probablemente lo que haría ahora si tuviera que abordar este problema. =] Hice la pregunta hace cuatro años (!) Cuando tenía poca comprensión de C#. Gracias por agregar tu respuesta, sin embargo; ¡Estoy seguro de que será útil para alguien en el futuro! – strager

Cuestiones relacionadas