'How to determine if an event is already subscribed

In my .NET application I am subscribing to events from another class. The subscription is conditional. I am subscribing to events when the control is visible and de-subscribing it when it become invisible. However, in some conditions I do not want to de-subscribe the event even if the control is not visible as I want the result of an operation which is happening on a background thread.

Is there a way through which I can determine if a class has already subscribed to that event?

I know we can do it in the class which will raise that event by checking the event for null, but how do I do it in a class which will subscribe to that event?



Solution 1:[1]

Assuming that you have no access to the innards of the class declaring the event, you have no way to do it directly. Events only expose operators += and -=, nothing else. You will need a flag or some other mechanism in your subscribing class to know whether you are already subscribed or not.

Solution 2:[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);
  }

Solution 3:[3]

Simply check whether the control is visible or not whenever the event handler is triggered.

Solution 4:[4]

Can you put the decision making logic into the method that fires the event? Assuming you're using Winforms it'd look something like this:

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

Where isCriteriaFulfilled is determined by your visible/invisible logic.

// UPDATES /////

Further to your 1st comment would it not make sense to alter the behaviour inside your event handler depending on the value of this.Visible?

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

Or if you really have to go with subscribing and unsubscribing:

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

Solution 5:[5]

using System;

//...

public event EventHandler Event;

public bool IsSubscribed(EventHandler Delegate)
{
    if (Event == null)
    {
        return false;
    }

    var InvocationList = Event.GetInvocationList();

    foreach (var Entry in InvocationList)
    {
        if (Entry.Equals(Delegate))
        {
            return true;
        }
    }

    return false;
}

After 12 years it is here, works for me.

Solution 6:[6]

Can't you just remember whether you already subscribed? That approach worked fine for me so far. Even if you have a lot of events or objects, you may still want to just remember that (in a dictionary, for example).

On the other hand, visibility change was, at least for me, not a good point to subscribe/unsubscribe. I typically rather go with construction / Disposed, which are more clear than each time visibility changes.

Solution 7:[7]

I'm just expanding on Hans' answer. I'm just trying to ensure that I'm not installing my handler more than once, and not removing it when I still need it. This doesn't protect from a malicious or malfeasant caller unsubscribing repeatedly, for that you'd need to track the callers, and that would just open you up to having repeated subscriptions overrun the tracking mechanism.

// 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;
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Gorpik
Solution 2 SwDevMan81
Solution 3 Peter Mortensen
Solution 4
Solution 5 Theodor Zoulias
Solution 6 OregonGhost
Solution 7 Darrel Lee