How do I get the subscribers of an event?
Asked Answered
K

4

33

I need to copy the subscribers of one event to another event. Can I get the subscribers of an event (like MyEvent[0] returning a delegate)?

If this is not possible I would use the add accessor to add the delegates to a list. Would that be the best solution?

Krasner answered 21/2, 2009 at 8:44 Comment(0)
D
38

C# events/delegates are multicast, so the delegate is itself a list. From within the class, to get individual callers, you can use:

if (field != null) 
{ 
    // or the event-name for field-like events
    // or your own event-type in place of EventHandler
    foreach(EventHandler subscriber in field.GetInvocationList())
    {
        // etc
    }
}

However, to assign all at once, just use += or direct assignment:

SomeType other = ...
other.SomeEvent += localEvent;
Damning answered 21/2, 2009 at 9:10 Comment(2)
Thanks for pointing that out. That's best for events in my own code.Krasner
Thanks so much for this. I needed a good workaround for object cloning through binary serialization not enjoying subscribed events, or else I'd have had to implement ICloneable in several hundred classes.Default
K
14

If the event is one published by another class, you can't - at least, not reliably. While we often think of an event as being just a delegate variable, it's actually just a pair of methods: add and remove (or subscribe and unsubscribe).

If it's your own code that's publishing the event, it's easy - you can make the add/remove accessors do whatever you like.

Have a look at my article on events and see if that helps you. If not, please give more details about what you want to do, specifying which bits of code you're able to modify and which you aren't.

Khalkha answered 21/2, 2009 at 8:56 Comment(0)
B
8

In case you need to examine subscribers of an external class' event:

EventHandler e = typeof(ExternalClass)
    .GetField(nameof(ExternalClass.Event), BindingFlags.Instance | BindingFlags.NonPublic)
    .GetValue(instanceOfExternalClass) as EventHandler;
if (e != null)
{
    Delegate[] subscribers = e.GetInvocationList();
}
Balkh answered 3/11, 2017 at 13:57 Comment(0)
H
3

Update (thanks to commenters): delegate immutability means that cloning achieves nothing over an assignment.

When one writes:

myDelegate += AHandler

A completely new delegate instance is created and assigned to myDelegate.

Therefore, the code below would work exactly the same without the Clone call.


MulticastDelegate (the underlying type) has a Clone method.

To be able to get to the underlying delegate you might need to avoid the usual helper that the event keyword generates, and manage things directly (custom add and remove accessors).

To show this:

public class Program {
    public delegate void MyDelegate(string name);

    public event MyDelegate EventOne;

    public void HandlerOne(string name) => Console.WriteLine($"This is handler one: {name}");
    public void HandlerTwo(string name) => Console.WriteLine($"This is handler two: {name}");
    public void HandlerThree(string name) => Console.WriteLine($"This is handler three: {name}");

    public void Run() {
        EventOne += HandlerOne;
        EventOne += HandlerTwo;
        Console.WriteLine("Before clone");
        EventOne("EventOne");

        MyDelegate eventTwo = (MyDelegate)EventOne.Clone();
        MyDelegate eventTwo = EventOne;
        Console.WriteLine("After clone copy");
        EventOne("EventOne");
        eventTwo("eventTwo");

        Console.WriteLine("Change event one to show it is different");
        EventOne += HandlerThree;
        EventOne("EventOne");
        eventTwo("eventTwo");
    }
    private static void Main(string[] args) => (new Program()).Run();
}
Hearken answered 21/2, 2009 at 8:53 Comment(5)
Clone itself is relatively unimportant - delegates are immutable, so you can just copy the delegate reference.Khalkha
Unless, as in the sample, you want to then modify the original or copy independently.Hearken
Richard - no, that would work identically without the Clone() step.Damning
Also, re "To be able to get to the underlying delegate" - the C# spec dictates that for field-like events (the simple ones), then inside the class any reference to the event-name is talking to the delegate field.Damning
Marc: of course. (Not enough coffee yet). (And easy to forget that += is .NET is not the shortcut in other languages).Hearken

© 2022 - 2024 — McMap. All rights reserved.