Button.Click as EventHandler via reflection [why not possible?]

  • Thread starter Thread starter Wiktor Zychla
  • Start date Start date
W

Wiktor Zychla

Let's start with an example: if you have:

class Test
{
public delegate int MyDelegate( int n );
event MyDelegate MyEvent;
}

then you can get MyEvent using reflection:

Test test = new Test(...);
Type t = typeof( Test );
t.InvokeMember( "MyEvent", BindingFlags.Instance | BindingFlags.GetField |
BindingFlags.NonPublic, null, test, null );

this works like a charm, you get reference to the delegate of type
MyDelegate.

Then comes the problem: why following does not work:

class MyForm : Form
{
Button b;
...
}

Type t = typeof( Button );
t.InvokeMember( "Click", BindingFlags.Instance | BindingFlags.GetField |
BindingFlags.NonPublic, null, b, null );

this example looks like a perfect copy of above one. Click event is declared
as field in Control class and thus, according to my intuition, should be
available via reflection. I would expect to get a delegate of type
EventHandler. instead an exception of missing member is thrown.

I just hope I miss something obvious. If not, I really would like to hear a
technical explanation, can be complicated, I am not afraid [I know how
delegates and events work at IL level].

Thanks in advance,
Wiktor Zychla
 
Hi Wiktor,

Interesting example.
The behaviour you're observing must be due to the fact that the
Click event of the Button class isn't implemented as a delegate field.
Though events are usually implemented that way (as in your first
example) alternative ways are possible.

Paragraph 17.7.2 of the C# languague specification (the ECMA version)
talks about manually implementing event accessors:

"One situation for doing so involves the case in which the storage cost
of one field per event is not acceptable. In such cases, a class can include
event-accessor-declarations and use a private mechanism for storing the
list of event handlers."

- Magnus
 
Interesting example.
The behaviour you're observing must be due to the fact that the
Click event of the Button class isn't implemented as a delegate field.
Though events are usually implemented that way (as in your first
example) alternative ways are possible.

Paragraph 17.7.2 of the C# languague specification (the ECMA version)
talks about manually implementing event accessors:

"One situation for doing so involves the case in which the storage cost
of one field per event is not acceptable. In such cases, a class can
include
event-accessor-declarations and use a private mechanism for storing the
list of event handlers."


brilliant!!! I've decompiled System.Windows.Forms to find out that this is
true: the Click event is handled via two methods: add_Click and
remove_Click. I've decompiled these methods. this is the result of applying
this knowledge to the Button example:


Type t = typeof(System.ComponentModel.Component);
System.ComponentModel.EventHandlerList eh =
(System.ComponentModel.EventHandlerList)t.InvokeMember( "Events",
BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.NonPublic,
null, b, null );



now I can see all handlers assigned to the event: the EventHandlerList has
private property 'head' that points to the first handler and each handler
has 'next' property that points to next handler.



thanks for the idea Magnus!



Wiktor Zychla
 
Back
Top