Cómo determinar si un evento ya está suscrito

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.

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

Sé que podemos hacerlo en la clase que provocará ese evento al verificar el evento para null , pero ¿cómo lo hago en una clase que se suscribirá a ese evento?

La palabra clave del event fue inventada explícitamente para evitar que haga lo que desea hacer. Restringe el acceso al objeto de delegate subyacente de modo que nadie pueda interferir directamente con las suscripciones del manejador de eventos que almacena. Los eventos son accesadores 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 una EventHandlerList con una “cookie” secreta como clave, debe conocer la cookie para extraer el objeto de la lista.

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

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

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 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.

  ///  /// Determine if a control has the event visible subscribed to ///  /// The control to look for the VisibleChanged event /// True if the control is subscribed to a VisibleChanged event, False otherwise 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); } 

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

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

Dónde está isCriteriaFulfilled está determinado por su lógica visible / invisible.

// ACTUALIZACIONES /////

Además de su primer comentario, ¿no tendría sentido alterar el comportamiento dentro de su controlador de eventos dependiendo del valor de this.Visible ? ¿ this.Visible ?

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

O si realmente tiene que seguir 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; } 

Simplemente verifique si el control está visible o no siempre que se active el controlador de eventos.

¿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.

Solo estoy expandiendo 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; ///  /// Register or unregister the ReflectionOnlyResolveHandler. ///  ///  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; } }