2010-04-23 9 views
43

En mi aplicación .NET me suscribo a eventos de otra clase. La suscripción es condicional. Me suscribo a eventos cuando el control es visible y lo retiro de la suscripción cuando se vuelve invisible. Sin embargo, en algunas condiciones, no deseo retirar la suscripción del evento, incluso si el control no está visible, ya que quiero el resultado de una operación que está sucediendo en una cadena de fondo.Cómo determinar si un evento ya está suscrito

¿Hay alguna manera de determinar si una clase ya se ha suscrito a ese evento?

Sé que podemos hacerlo en la clase que planteará ese evento marcando el evento para null, pero ¿cómo puedo hacerlo en una clase que se suscribirá a ese evento?

+2

Comprobar este enlace http://social.msdn.microsoft.com/forums/en-US/csharpgeneral/thread/d7d8791f-6aef-4fda-ae0e-5eddcb856706/ –

+0

si es sólo un carnero * * nadie está suscrito:'bool subscribedTo = theEvent! = null' – Mark

Respuesta

51

La palabra clave event se inventó explícitamente para evitar que haga lo que quiere hacer. Restringe el acceso al objeto subyacente delegate para que nadie pueda meterse directamente con las suscripciones del manejador de eventos que almacena. Los eventos son accesors para un delegado, al igual que una propiedad es un descriptor de acceso para un campo. Una propiedad solo permite obtener y establecer, un evento solo permite agregar y eliminar.

Esto mantiene su código a salvo, otro código solo puede eliminar un controlador de eventos si conoce el método del controlador de eventos y el objeto de destino. El lenguaje C# pone una capa adicional de seguridad en su lugar al no permitirle nombrar el objeto de destino.

Y WinForms pone una capa adicional de seguridad en su lugar por lo que se vuelve difícil incluso si usa Reflection.Almacena las instancias delegate en un EventHandlerList con una "cookie" secreta como clave, debe conocer la cookie para extraer el objeto de la lista.

Bueno, no vayas allí. Es trivial para resolver su problema con un poco de código en su final:

private bool mSubscribed; 

private void Subscribe(bool enabled) 
{ 
    if (!enabled) textBox1.VisibleChanged -= textBox1_VisibleChanged; 
    else if (!mSubscribed) textBox1.VisibleChanged += textBox1_VisibleChanged; 

    mSubscribed = enabled; 
} 
+4

¿Tendría algún blog, videos de canal 9 o artículos de msdn que hablen sobre la metodología de diseño que hace que los * eventos * sean tan difíciles de interactuar? Quizás si entendí el * por qué * y los mecanismos * pretendidos * para lograr algunas cosas (normalmente) triviales, podría tener un tiempo más fácil para encontrar soluciones triviales a mis propios conjuntos de problemas. –

+1

Sus eventos no son difíciles de interactuar. Meterse con los eventos de * alguien más * es difícil, eso es jugar con partes privadas. Haga una pregunta al respecto. –

+2

Bien, * ¿por qué * es difícil jugar con los eventos de otra persona? –

0

¿No puedes recordar si ya te suscribiste? Ese enfoque funcionó bien para mí hasta ahora. Incluso si tiene muchos eventos u objetos, tal vez quiera recordar eso (en un diccionario, por ejemplo).

Por otro lado, el cambio de visibilidad fue, al menos para mí, un buen punto para suscribirse/darse de baja. Normalmente prefiero ir con la construcción/Dispuesto, que son más claros que cada vez que cambia la visibilidad.

6

Suponiendo que no tiene acceso a las entrañas de la clase que declara el evento, no tiene forma de hacerlo directamente. Los eventos solo exponen a los operadores += y -=, nada más. Necesitará una bandera u otro mecanismo en su clase de suscripción para saber si ya está suscrito o no.

1

¿Puede poner la lógica de la toma de decisiones en el método que dispara el evento? Suponiendo que está usando Windows Forms se vería algo como esto:

if (MyEvent != null && isCriteriaFulfilled) 
{ 
    MyEvent(); 
} 

Dónde isCriteriaFulfilled está determinada por la lógica visible/invisible.

// ACTUALIZACIONES /////

En respuesta a su primera comentario sería que no tenga sentido para alterar el comportamiento dentro de su controlador de eventos en función del valor de this.Visible?

a.Delegate += new Delegate(method1); 
... 
private void method1() 
{ 
    if (this.Visible) 
     // Do Stuff 
} 

O si realmente tiene que ir con suscribirse y darse de baja:

private Delegate _method1 = null; 
... 
if(this.visible) 
{ 
    if (_method1 == null) 
     _method1 = new Delegate(method1); 
    a.Delegate += _method1; 
} 
else if (_method1 != null) 
{ 
    a.Delegate -= _method1; 
} 
+1

Me pregunto si 'areCriteriaFulfilled' es mejor hablando gramaticalmente? –

+0

Estoy haciendo lo siguiente if (this.visible) { a.Delegate + = new Delegate (method1); } else { a.Delegate - = new Delegate (method1); } – Ram

+0

@Ram: respuesta actualizada. –

1

Basta con comprobar si el control es visible o no cada vez que se activa el controlador de eventos.

+0

No quiero hacer eso ya que los eventos se desencadenan a intervalos regulares y quiero usarlos solo cuando mi control es invisible. si hago lo que dices, será un golpe de rendimiento. – Ram

+0

@Ram: ¿Por qué crees que será un golpe de rendimiento? ¿Has medido el cambio en el rendimiento? –

+0

@ Phil: Hola Phil, es un éxito de rendimiento ya que estoy haciendo esto con múltiples formas y múltiples eventos. Cada formulario procesa la forma de diferencia de datos. Por lo tanto, para evitar el procesamiento de datos, me suscribo a los eventos, solo el formulario es visible. Creo que usar boolean sería una buena opción. – Ram

2
/// <summary> 
    /// Determine if a control has the event visible subscribed to 
    /// </summary> 
    /// <param name="controlObject">The control to look for the VisibleChanged event</param> 
    /// <returns>True if the control is subscribed to a VisibleChanged event, False otherwise</returns> 
    private bool IsSubscribed(Control controlObject) 
    { 
    FieldInfo event_visible_field_info = typeof(Control).GetField("EventVisible", 
     BindingFlags.Static | BindingFlags.NonPublic); 
    object object_value = event_visible_field_info.GetValue(controlObject); 
    PropertyInfo events_property_info = controlObject.GetType().GetProperty("Events", 
     BindingFlags.NonPublic | BindingFlags.Instance); 
    EventHandlerList event_list = (EventHandlerList)events_property_info.GetValue(controlObject, null); 
    return (event_list[object_value] != null); 
    } 
0

Sólo estoy ampliando sobre la respuesta de Hans. Solo estoy tratando de asegurarme de no instalar mi controlador más de una vez, y no eliminarlo cuando aún lo necesito. Esto no protege de que un llamante malicioso o mal intencionado se desinscriba repetidamente, para eso necesitaría rastrear a las personas que llaman, y eso simplemente lo abriría a que las suscripciones repetidas invadieran el mecanismo de seguimiento.

// Tracks how many times the ReflectionOnlyResolveHandler has been requested. 
private static int _subscribers = 0; 

/// <summary> 
/// Register or unregister the ReflectionOnlyResolveHandler. 
/// </summary> 
/// <param name="enable"></param> 
public static void SubscribeReflectionOnlyResolve(bool enable) 
{ 
    lock(_lock) 
    { 
     if (_subscribers > 0 && !enable) _subscribers -= 1; 
     else if (enable) _subscribers += 1; 

     if (enable && _subscribers == 1) 
      AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += ReflectionHelper.ReflectionOnlyResolveHandler; 
     else if (_subscribers == 0) 
      AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= ReflectionHelper.ReflectionOnlyResolveHandler; 
    } 
} 
+0

Las personas que llaman arriba no son realmente "suscriptores", solo le están pidiendo a la clase auxiliar que registre su manejador. –

Cuestiones relacionadas