2009-08-07 3 views
30

estoy seguro de que estoy simplemente no entender algo fundamental acerca de eventos y/o delegados en C#, pero por qué no puedo hacer las pruebas de Boole en este ejemplo de código:En C#, ¿por qué no puedo probar si un controlador de eventos es nulo en algún lugar fuera de la clase que está definida?

public class UseSomeEventBase { 
    public delegate void SomeEventHandler(object sender, EventArgs e); 
    public event SomeEventHandler SomeEvent; 
    protected void OnSomeEvent(EventArgs e) { 
     // CANONICAL WAY TO TEST EVENT. OF COURSE, THIS WORKS. 
     if (SomeEvent != null) SomeEvent(this, e); 
    } 
} 

public class UseSomeEvent : UseSomeEventBase { 
    public bool IsSomeEventHandlerNull() { 
     // "LEFT HAND SIDE" COMPILER ERROR 
     return SomeEvent == null; 
    } 
} 

class Program { 
    static void Main(string[] args) { 
     var useSomeEvent = new UseSomeEvent(); 
     useSomeEvent.SomeEvent +=new UseSomeEventBase.SomeEventHandler(FuncToHandle); 
     // "LEFT HAND SIDE" COMPILER ERROR 
     if (useSomeEvent.SomeEvent == null) { 

     } 
     var useSomeEventBase = new UseSomeEventBase(); 
     useSomeEventBase.SomeEvent += new UseSomeEventBase.SomeEventHandler(FuncToHandle); 
     // "LEFT HAND SIDE" COMPILER ERROR 
     if (useSomeEventBase.SomeEvent == null) { 

     } 
    } 

    static void FuncToHandle(object sender, EventArgs e) { } 
} 

Respuesta

55

un evento es en realidad una operación "agregar" y una operación "eliminar". No puede obtener el valor, no puede establecer el valor, no puede llamarlo, puede suscribir un controlador para el evento (add) o cancelar la suscripción (remove). Esto está bien, es encapsulación, simple y llana. Depende del editor implementar agregar/eliminar de manera apropiada, pero a menos que el editor decida hacer que los detalles estén disponibles, los suscriptores no pueden modificar ni acceder a las partes específicas de la implementación.

eventos de campo como en C# (donde no se especifica el add/remove bits) ocultan esto - que crean una variable de un tipo de delegado y un evento. Las implementaciones de agregar/eliminar del evento solo usan la variable para realizar un seguimiento de los suscriptores.

Dentro de la clase se refiere a la variable (para que pueda obtener los delegados suscritos actualmente, ejecutarlos, etc.) y fuera de la clase se refiere al evento en sí (así que solo tiene capacidades de agregar/eliminar).

La alternativa a los eventos de tipo campo es donde implementa explícitamente el agregar/eliminar usted mismo, p. Ej.

private EventHandler clickHandler; // Normal private field 

public event EventHandler Click 
{ 
    add 
    { 
     Console.WriteLine("New subscriber"); 
     clickHandler += value; 
    } 
    remove 
    { 
     Console.WriteLine("Lost a subscriber"); 
     clickHandler -= value; 
    } 
} 

Ver my article on events para obtener más información.

Por supuesto, el publicador de eventos puede también facilitar más información - se podría escribir una propiedad como ClickHandlers para devolver el actual delegado de multidifusión, o HasClickHandlers volver si existen o no. Sin embargo, eso no forma parte del modelo de evento central.

+0

+1 para la respuesta. Solo para agregarlo, puede, por supuesto, implementar las operaciones de agregar/eliminar como métodos explícitos con un delegado de multidifusión internamente. En ese caso, puede rastrear suscriptores y no suscriptores usted mismo, y así proporcionar acceso a información adicional (por ejemplo, recuento de suscriptores, etc.) si así lo desea. – LBushkin

+0

Sí, lo agregaré, gracias. –

+0

Podría ser útil tener en cuenta que los eventos * reason * se implementan de esta forma para que cualquier clase no pueda cambiar el comportamiento de la clase con el evento simplemente accediendo al evento (como una propiedad de delegado) y borrando su lista de suscriptores – womp

0

Tendría que hacer eso desde la clase base. Esa es la razón exacta por la que hizo esto:

protected void OnSomeEvent(EventArgs e) { 
    // CANONICAL WAY TO TEST EVENT. OF COURSE, THIS WORKS. 
    if (SomeEvent != null) SomeEvent(this, e); 
} 

No puede acceder a los eventos de una clase derivada. Además, debe hacer que ese método sea virtual, para que pueda ser anulado en una clase derivada.

+2

Tenga en cuenta que eso no es inseguro. Puede lanzar una NullReferenceException si el último controlador se elimina después de la prueba, pero antes de la invocación. –

+0

@Jon - Leí su artículo sobre esto, pero no creo que esto suceda en esta aplicación web. – gabe

2

Aquí es una pregunta un poco diferente

¿Qué valor tiene la prueba de un evento definido externamente para nula?

Como un consumidor externo de un evento que sólo puede hacer 2 operaciones

  • Añadir un controlador
  • Retirar un manejador

La no nullness nulo o del evento tiene no tiene relación con estas 2 acciones. ¿Por qué quieres ejecutar una prueba que no proporcione ningún valor perceptible?

+0

estoy tratando de escribir una clase de trabajo de envoltura para 2 controles web de terceros mal diseñados que debo usar para un proyecto (odio estos controles locos). Estoy intentando imponer el uso de un método específico como el controlador de eventos para un evento en un objeto miembro (referencia a uno de los controles web) de una clase (el contenedor). Quiero hacer una comprobación en la clase contenedora para garantizar que el usuario de la clase contenedora haya agregado un controlador de eventos específico a un evento del objeto miembro. es muy específico para estos 2 controles estúpidos y difícil de explicar ... – gabe

1

Es una regla en su lugar cuando se utiliza la palabra clave 'evento'.Cuando crea un evento, restringe la interacción fuera de clase con el delegado a una relación de "suscripción/desinscripción", esto incluye casos de herencia. Recuerde que un evento es esencialmente una propiedad, pero para las llamadas de método, no es realmente un objeto en sí mismo, lo que en realidad se parece más a esto:

public event SomeEventHandler SomeEvent 
{ 
    add 
    { 
      //Add method call to delegate 
    } 
    remove 
    { 
      //Remove method call to delegate 
    } 
} 
14

puede utilizar simplemente un enfoque muy simple para no suscribirse en varias ocasiones a un evento.

Cualquiera de los 2 enfoques de abajo se pueden utilizar:

  1. enfoque Bandera: _getWarehouseForVendorCompletedSubscribed es una variable privada inicializado a falso.

    if (!_getWarehouseForVendorCompletedSubscribed) 
        { 
         _serviceClient.GetWarehouseForVendorCompleted += new EventHandler<GetWarehouseForVendorCompletedEventArgs>(_serviceClient_GetWarehouseForVendorCompleted); 
         _getWarehouseForVendorCompletedSubscribed = true; 
        } 
    
  2. Enfoque Darse de baja: Incluir un darse de baja cada vez que desea suscribirse.

    _serviceClient.GetWarehouseForVendorCompleted -= new 
        EventHandler<GetWarehouseForVendorCompletedEventArgs> 
    (_serviceClient_GetWarehouseForVendorCompleted); 
    
    
    _serviceClient.GetWarehouseForVendorCompleted += new 
         EventHandler<GetWarehouseForVendorCompletedEventArgs> 
         (_serviceClient_GetWarehouseForVendorCompleted); 
    
+0

+1 Esto es BRILLANTE y esto es exactamente lo que estaba buscando.No entiendo por qué esto no obtuvo más votos –

+0

Para el enfoque de cancelación de suscripción, ¿cómo se puede 'eliminar' un nuevo EventHandler si aún no se ha agregado? – noahnu

+3

Si no se ha agregado, simplemente ignorará el signo - = –

3

Aquí la respuesta:

using System; 
delegate void MyEventHandler(); 
class MyEvent 
{ 
    string s; 
    public event MyEventHandler SomeEvent; 
    // This is called to raise the event. 
    public void OnSomeEvent() 
    { 
     if (SomeEvent != null) 
     { 
      SomeEvent(); 
     } 

    } 

    public string IsNull 
    { 
     get 
     { 
      if (SomeEvent != null) 
       return s = "The EventHandlerList is not NULL"; 
      else return s = "The EventHandlerList is NULL"; ; 
     } 
    } 
} 

class EventDemo 
{ 
    // An event handler. 
    static void Handler() 
    { 
     Console.WriteLine("Event occurred"); 
    } 

    static void Main() 
    { 
     MyEvent evt = new MyEvent(); 
     // Add Handler() to the event list. 
     evt.SomeEvent += Handler; 
     // Raise the event. 
     //evt.OnSomeEvent(); 
     evt.SomeEvent -= Handler; 
     Console.WriteLine(evt.IsNull); 
     Console.ReadKey(); 
    } 
} 
0

Editorial del evento de sobrecarga implícitamente sólo += y -= operaciones, y otras operaciones no se implementan en el editor debido a las razones obvias como se explicó anteriormente , como no querer dar control al suscriptor para cambiar eventos.

Si queremos validar si un evento en particular está suscrito en la clase de suscriptor, mejor editor establecerá un indicador en su clase cuando el evento sea suscriptor y borrará el indicador cuando sea suscriptor.

Si el suscriptor puede acceder a la bandera del editor, puede identificar fácilmente si el evento en particular es suscriptor o no al verificar el valor de la bandera.

Cuestiones relacionadas