views:

62

answers:

1

When using a Delegate, I can use Delegate.GetInvocationList() Method to retrieve the invocation list of the delegate at runtime.

Is there a way to access the subscriber list that has been associated with an event? I ran the code from this example (SO#1237001) and the immediate window allowed me to cast the SecondChange event to a System.MultiCastDelegate and then invoke the GetInvocationList method.

However, in my scenario I am working with a System.Windows.Forms.DataGridView and I would like to inspect the invocation list of the CellClick event at runtime. However, when I try any kind of cast on CellClick, I recieve the following error:

The event 'System.Windows.Forms.DataGridView.CellClick' can only appear on the left hand side of += or -=

I can see there are clearly differences in the declarations of these events. In the Clock example the event is declared like this:

public event Func<DateTime, bool> SecondChange;

And in the DataGridView the event is declared like this:

[SRDescription("DataGridView_CellClickDescr"), SRCategory("CatMouse")]
public event DataGridViewCellEventHandler CellClick
{
    add
    {
        base.Events.AddHandler(EVENT_DATAGRIDVIEWCELLCLICK, value);
    }
    remove
    {
        base.Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLCLICK, value);
    }
}

Why can one call GetInvocationList on the Clock example, but not on the DataGridView event? Is there any way for me to get the same type of information from the DataGridView event that GetInvocationList returns?

+3  A: 

The whole point of an event it defines (only) an add/remove API (like how a property defines get/set).

The entire point of an event is that externally you can't do this (access the subscribers). I'm guessing that in the "clock" example, the code that accesses the list is inside the type that declares the event; that is fine: inside the type, you have full access to the backing implementation (often a delegate field).

Externally, you should only care about your own handlers, that you already know about because you subscribed them. Attempts to fetch the backer exist, but it is brittle and not recommended. In this case, it uses an EventHandlerList, for example.

Why do you need this? It usually means that you are doing something wrong (sorry, but it does).

Marc Gravell
The most common reason I've seen to access the subscriber list is to perform a global unsubscribe. This can definitely be useful in certain situations (specifically those where an external controller wants to "retire" an object but cannot because listeners are still subscribed). A consistent `removeAll` mechanism on events would be useful.
LBushkin
Well, it could just release the object to the void... events keep subscribers alive, not the other way around. Unless they have a reference somewhere else, no amount of subscribers will keep an object alive.
Marc Gravell
I just wanted to write a unit test that ensured my object had subscribed to the DataGridView's event. Maybe I'm looking at the problem from the wrong direction, or perhaps this test isn't something worth writing.
magnifico
Perhaps do something to cause the event to get fired? If necessary, use reflection to invoke the protected `OnCellClick` method?
Marc Gravell
The other option is to use reflection to get `Events`, use reflection to get `EVENT_DATAGRIDVIEWCELLCLICK`, and combine. Not pretty, and brittle (in theory, at least; it is unlikely to change) .
Marc Gravell
Thanks Marc, I was looking at retrieving EVENT_DATAGRIDVIEWCELLCLICK and Events, but I could tell I was fighting against the system. I thought there might be a better way once I saw the clock example, but like you said, that only works from within the type.
magnifico