2010-12-03 19 views
12

Tengo tres objetos ObjectA tiene un ObjectB, ObjectB tiene un ObjectC. Cuando ObjectC dispara un evento que necesito Objecta a saber sobre él, así que esto es lo que he hecho ...¿Cuál es la forma preferida de eventos de burbujas?

public delegate void EventFiredEventHandler(); 

public class ObjectA 
{ 
    ObjectB objB; 

    public ObjectA() 
    { 
     objB = new ObjectB(); 
     objB.EventFired += new EventFiredEventHandler(objB_EventFired); 
    } 

    private void objB_EventFired() 
    { 
     //Handle the event. 
    } 
} 

public class ObjectB 
{ 
    ObjectC objC; 

    public ObjectB() 
    { 
     objC = new ObjectC(); 
     objC.EventFired += new EventFiredEventHandler(objC_EventFired); 
     objC.FireEvent(); 
    } 

    public event EventFiredEventHandler EventFired; 
    protected void OnEventFired() 
    { 
     if(EventFired != null) 
     { 
      EventFired(); 
     } 
    } 

    private void objC_EventFired() 
    { 
      //objC fired an event, bubble it up. 
     OnEventFired(); 
    } 
} 

public class ObjectC 
{ 
    public ObjectC(){} 

    public void FireEvent() 
    { 
     OnEventFired(); 
    } 

    public event EventFiredEventHandler EventFired; 
    protected void OnEventFired() 
    { 
     if(EventFired != null) 
     { 
      EventFired(); 
     } 
    } 
} 

Es esta la forma correcta de manejar esto, o hay una mejor manera? No quiero que ObjectA sepa nada sobre ObjectC, solo que generó un evento.

+1

Esta es una manera efectiva de hacerlo. – jvanrhyn

Respuesta

16

Otro enfoque, es envolver mediante Agregar/Quitar:

public class ObjectB 
{ 
    ObjectC objC; 

    public ObjectB() 
    { 
     objC = new ObjectC(); 
    } 

    public event EventFiredEventHandler EventFired 
    { 
     add { this.objC.EventFired += value; } 
     remove { this.objC.EventFired -= value; } 
    } 
} 
+0

Esto parece que podría ahorrar algo de tiempo si hubiera un montón de eventos para hacer burbujas. Gracias. – Tester101

3

Así es como lo hago. No obstante recomendaría cambiar su mecanismo de disparo a este para que sea seguro ante los threads

protected void OnEventFired() 
{ 
    var tmpEvent = EventFired; 
    if(tmpEvent != null) 
    { 
     tmpEvent(); 
    } 
} 

Esto evita que se convierte en su defecto, si EventFired nulo entre el cheque nulo y el despido.

También es un poco estándar seguir el EventHandler pattern para los delegados de su evento.

protected virtual void OnEventFired(EventArgs e) 
{ 
    var tmpEvent = EventFired; 
    if(tmpEvent != null) 
    { 
     tmpEvent(this, EventArgs.e); 
    } 
} 

estaba equivocado sobre el patrón multi-hilo, aquí es el patrón de eventos multi-hilo completo

/// <summary> 
/// Delegate backing the SomeEvent event. 
/// </summary> 
SomeEventHandler someEvent; 

/// <summary> 
/// Lock for SomeEvent delegate access. 
/// </summary> 
readonly object someEventLock = new object(); 

/// <summary> 
/// Description for the event 
/// </summary> 
public event SomeEventHandler SomeEvent 
{ 
    add 
    { 
     lock (someEventLock) 
     { 
      someEvent += value; 
     } 
    } 
    remove 
    { 
     lock (someEventLock) 
     { 
      someEvent -= value; 
     } 
    } 
} 

/// <summary> 
/// Raises the SomeEvent event 
/// </summary> 
protected virtual OnSomeEvent(EventArgs e) 
{ 
    SomeEventHandler handler; 
    lock (someEventLock) 
    { 
     handler = someEvent; 
    } 
    if (handler != null) 
    { 
     handler (this, e); 
    } 
} 
+0

¿Por qué no lo haría la manera OP es segura para hilos? –

+0

@Mike Cheel Como dije en la publicación, si el evento se cancela entre la verificación nula y la activación real, se lanzará una excepción (NullRefrence, creo). Al asignar el evento a una variable separada, el evento continuará activando esa última vez si se anuló la suscripción en el medio de esta función. –

+0

¿No es la firma habitual 'vacío virtual protegido OnMyEvent (MyEventArgs e)'? – Greg

1

Como han indicado otras respuestas, esta es su manera de hacerlo.

Pero puedes ir más allá !!! Acabo de implementar una buena estructura de datos y es como darle un giro.

Sería bueno tener un evento automático de burbujeo? Puedes implementarlo usando Reflection. Mi manera es definir una clase Interface/Base que declare un evento (o un conjunto de eventos). Entonces, el constructor sin parámetros de una clase base repetirá otras propiedades/campos y registrará automáticamente los eventos de los miembros para la propagación del evento.

Existen algunas restricciones en el diseño, pero si tiene una estructura profunda y/o muchos eventos (estructurados), podría ser bueno tener todo configurado sin ninguna línea adicional de código.

Una clase base inicial podría ser:

class BaseObject { 
    public BaseObject() { 
     FieldInfo[] fInfos = this.GetType().GetFields(...); 

     foreach (FieldInfo fInfo in fInfos) { 
      object fInfoValue = fInfo.GetValue(this, null); 
      if (fInfoValue is BaseObject) { 
       BaseObject bMemberObject = (BaseObject)fInfoValue; 

       bMemberObject.MyEvent += new EventHandler(delegate() { 
        if (this.MyEvent != null) 
         MyEvent(); 
       }); 
      } 
    } 

    public event MyEvent = null; 

} 

Por supuesto, como ya se ha sugerido, seguir el evento delegado delegado (object sender, EventArgs args) (He usado un evento más simple para mayor claridad) . Naturalmente, queda implícito que le clases A, B y C deriva directamente de BaseObject.

Tenga en cuenta que cualquier lógica podría ser implementado para enlazar eventos estructurados (que podría ser el registro de eventos anidada utilizando el nombre y/o otras propiedades reflejadas.

Cuestiones relacionadas