2011-01-21 14 views
7

tengo la siguiente clase, que tiene un evento público llamado LengthChanged:Cómo hacer referencia a un evento en C#

class Dimension 
{ 
    public int Length 
    { 
     get 
     { 
      return this.length; 
     } 
     set 
     { 
      if (this.length != value) 
      { 
       this.length = value; 
       this.OnLengthChanged(); 
      } 
    } 

    protected virtual void OnLengthChanged() 
    { 
     var handler = this.LengthChanged; 
     if (handler != null) 
     { 
      handler (this, System.EventArgs.Empty); 
     } 
    } 

    public event System.EventHandler LengthChanged; 

    private int length; 
} 

me gustaría ser capaz de registrar/manipuladores de anular el registro de este evento en un método llamado Observer , que no sabe nada sobre la clase Dimension. He llegado con dos escenarios, ninguno de los cuales son realmente satisfactorio:

  1. definir una interfaz ILengthChanged con el evento LengthChanged, a continuación, asegúrese Dimension implementa ILengthChanged. Entonces, tengo que proporcionar una implementación del método Observer para cada interfaz que defina. Esto de ninguna manera lo suficientemente genérico. Realmente me gustaría poder pasar una referencia al evento System.EventHandler.

  2. Use System.Action<System.EventHandler> devoluciones de llamada para registrar y anular el registro del controlador de eventos en el método Observer, al igual que:

    clase Foo { Observador public void (registro System.Action, anular el registro System.Action) { registro (this.MyEventHandler);

    // keep track of the unregister callback, so that we can unregister 
        // our event handler later on, if needed... 
    } 
    
    private void MyEventHandler(object sender, System.EventArgs e) 
    { 
        ... 
    } 
    

    }

que luego se invoca así:

Foo foo = ...; 
Dimension dim = ...; 

foo.Observer (x => dim.LengthChanged += x, x => dim.LengthChanged -= x); 

y que, cuando se ejecuta, de hecho va a terminar el cableado del evento LengthChanged con el controlador de eventos internos MyEventHandler . Pero esto no es muy elegante. Me hubiera encantado poder escribir esto en su lugar:

Foo foo = ...; 
Dimension dim = ...; 

foo.Observer (dim.LengthChanged); 

pero no tengo idea de cómo se podría lograr esto. Tal vez me estoy perdiendo algo realmente obvio aquí? Supongo que un poco de magia dynamic podría hacer el truco, de alguna manera, pero esto no haría cumplir la verificación de tipos de tiempo de compilación: No quiero que los usuarios de Observer pasen referencias a eventos que no satisfacen la firma de evento System.EventHandler.

Respuesta

10

Desafortunadamente no hay realmente una manera de hacer esto. Los eventos no son ciudadanos de primera clase en .NET en general, aunque F # intenta promocionarlos allí.

Pase el delegado de suscripción/desuscripción o use una cadena que indique el nombre del evento. (Este último suele ser más corto, pero obviamente menos seguro en tiempo de compilación).

Esos son los enfoques que toma Reactive Extensions; si hubiera una manera más clara de hacerlo, estoy seguro de que estarían usando eso :(

+0

Si la gente como Erik Meijer no fueron capaces de llegar a soluciones más limpias, me sospecha que, de hecho, no hay otra manera de hacerlo. Eso es triste ... Gracias por su respuesta rápida, Jon. –

2

No se supone que el evento pase a otro método. Sin embargo, puede pasar el delegado a otro método. Quizás, lo que está buscando es simplemente un simple delegado público en lugar de un evento.

Si cambia su evento a esta

public System.EventHandler LengthChanged; 

Usted puede simplemente pasar el LengthChanged a Observador como esto

Foo foo = ...; 
Dimension dim = ...; 
foo.Observer (dim.LengthChanged); 
+0

Gracias por su sugerencia. No, no puedo pasar al delegado en lugar del evento; el evento podría implementar los setters 'add' y' remove' y realmente tiene que definirse como un 'evento' completo de C#. –

+0

@Pierre Ya veo ... en ese caso, lo que haría es declarar una clase contenedora delegada. El constructor de esa clase toma un delegado. La clase tiene AddHandler virtual() y RemoveHandler virtual(). También tiene un método Invoke() para llamar al delegado. Para que se vea más elegante, puede definir operator + =, - =, + y -, llamando a su AddHandler y RemoveHandler. Reemplace el evento LengthChanged de su dimensión por esta clase contenedora. Para el caso de que necesite "agregar" y "eliminar" los establecedores, simplemente anule los métodos AddHandler y RemoveHandler de esa clase contenedora. Solo mis 2 centavos. He hecho algo similar. –

+0

buena solución ... Tendré que pensarlo. +1 por este bonito truco. –

Cuestiones relacionadas