2

This question asks if there is a way to find if the code has already added its own event handler to an event. However, the answers given only work from inside the same class that own the event. (Delegate.GetInvocationList and others.)

I want to add a custom event handler to AppDomain.CurrentDomain.AssemblyResolve. Is there a way to find out if my custom handler is already added before adding it again? (For this and other standard library events.)

If the answer is indeed "That's impossible." then please give that as an answer.

Community
  • 1
  • 1
billpg
  • 3,195
  • 3
  • 30
  • 57

2 Answers2

5

That's impossible.

Basically, the only operations you have with an event from the outside are "subscribe" and "unsubscribe".

Now you could always unsubscribe before you subscribe. Unsubscribing is a no-op if the specified handler isn't already a handler for the event. If you make sure you always do that, then you'll definitely have exactly one handler subscribed. It does mean you need to be careful to do that everywhere you subscribe though - so ideally, put that code in one place.

(Alternatively, just change your event subscription so that you can easily tell that you'll only ever subscribe once...)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 3
    Thank you. If you think its impossible then it probably is. I hope you're comfortable being seen as infallible. :) – billpg Feb 13 '15 at 14:07
1

You can...

You can use reflection to get all delegates subscribed to the event and then check their names to see if yours is in there...

public class Foo
{
    public event EventHandler MyEvent;
}

public class Bar
{
    public static event EventHandler MyStaticEvent;
}

public class Test
{
    public void MyDelegate(object sender, EventArgs e) { }
}

class Program
{
    static void Main(string[] args)
    {
        Foo aFoo = new Foo();
        Test aTest = new Test();
        aFoo.MyEvent += aTest.MyDelegate;

        FieldInfo subscribersReflect = typeof(Foo).GetField("MyEvent", BindingFlags.NonPublic | BindingFlags.Instance);
        Delegate[] subscribers = (subscribersReflect.GetValue(aFoo) as MulticastDelegate).GetInvocationList();

        foreach (var sub in subscribers)
            Console.WriteLine(sub.Method.Name); // MyDelegate

        Bar.MyStaticEvent += aTest.MyDelegate;
        subscribersReflect = typeof(Bar).GetField("MyStaticEvent", BindingFlags.NonPublic | BindingFlags.Static);
        subscribers = (subscribersReflect.GetValue(null) as MulticastDelegate).GetInvocationList();

        foreach (var sub in subscribers)
            Console.WriteLine(sub.Method.Name); // MyDelegate

        Console.ReadLine();
    }
}

...but you really shouldn't.

Any time you find yourself tempted to use reflection to go digging around in another class, especially a class you don't have the source for, and super-especially a framework class, that should be a warning signal that you're doing something wrong.

Jon Skeet's solution (unsubscribe then subscribe) is absolutely the correct solution to your problem, and is a good habit to be in any case. As he mentioned, unsubscribing a delegate that isn't subscribed has effectively no cost whatsoever, so any time you're unsure, go ahead and unsubscribe. It was designed that way specifically so that you could do that rather than using reflection.

Oblivious Sage
  • 3,326
  • 6
  • 37
  • 58