Events and delegates

  • Thread starter Thread starter Sgt. Sausage
  • Start date Start date
S

Sgt. Sausage

NOTE: I'm new to c#, but not to programming.

For reasons unimportant to the discussion below, I need to be able
to dynamically, at run time, discover the set of all objects listening to
another object's events.

Given some jRandomType, I've figured out how to get the Events
themselves via reflection and GetEvents/EventInfo. This will give me
a list of the Events that jRandomType wants to raise -- but that's only
half the battle.

Now, the part that's got me stumped: Once I have a list of Events
that a Type raises, how do I get from there to iterating through the
subscribers that are listening to the Events on some instance of
jRandomType?

I understand that the Events are defined by the *class*, and the
listeners are registered on a particular *instance* of that class -- e.g.
a given class may raise a single Event, but three instances of that
class may have three different listeners registered. That's not a big
conceptual problem for me. But -- given that I can find the Events
raised via reflection, how do I find out what's subscribed as a
listener on a particular instance?

Is this a case of "you can't get there from here" ? Or, is there an
easy way I'm not finding in the docs?

Thanks for your help.
 
Sgt. Sausage said:
NOTE: I'm new to c#, but not to programming.

For reasons unimportant to the discussion below, I need to be able
to dynamically, at run time, discover the set of all objects listening to
another object's events.

Given some jRandomType, I've figured out how to get the Events
themselves via reflection and GetEvents/EventInfo. This will give me
a list of the Events that jRandomType wants to raise -- but that's only
half the battle.

Now, the part that's got me stumped: Once I have a list of Events
that a Type raises, how do I get from there to iterating through the
subscribers that are listening to the Events on some instance of
jRandomType?

I understand that the Events are defined by the *class*, and the
listeners are registered on a particular *instance* of that class -- e.g.
a given class may raise a single Event, but three instances of that
class may have three different listeners registered. That's not a big
conceptual problem for me. But -- given that I can find the Events
raised via reflection, how do I find out what's subscribed as a
listener on a particular instance?

Is this a case of "you can't get there from here" ? Or, is there an
easy way I'm not finding in the docs?

You can't in all cases. In some cases it's relatively straightforward.
If the event has been defined in C# as:

public event Foo;

then the compiler will have generated a field (also called "Foo" with
the MS compiler, but it's not guaranteed) of the appropriate delegate
type.

There's no guarantee that it's been done that way. For instance, you
could write a perverse event:

public event Amnesiac
{
add {}
remove {}
}

which basically doesn't act properly at all - it forgets whatever is
added to it!
 
You can't in all cases. In some cases it's relatively straightforward.
If the event has been defined in C# as:

public event Foo;

Assuming that the event is formed in a normal manner, though, you can
access the Method and Target parameters of the event delegates...

public event Event_Handler evt;
Delegate[] delArray = evt.GetInvocationList();
for(int i=0; i<delArray.Length; i++)
{
Delegate ev = delArray;
Trace.WriteLine(ev.Method);
Trace.WriteLine(ev.Target);
}

-mdb
 
Jon Skeet said:
[snip]
Is this a case of "you can't get there from here" ? Or, is there an
easy way I'm not finding in the docs?

You can't in all cases. In some cases it's relatively straightforward.
If the event has been defined in C# as:

public event Foo;

then the compiler will have generated a field (also called "Foo" with
the MS compiler, but it's not guaranteed) of the appropriate delegate
type.

That's not really any help now is it? <grin>

[snip]

So, in this case, how do I grab it and iterate through the listeners?
 
mdb said:
You can't in all cases. In some cases it's relatively straightforward.
If the event has been defined in C# as:

public event Foo;

Assuming that the event is formed in a normal manner, though, you can
access the Method and Target parameters of the event delegates...

public event Event_Handler evt;
Delegate[] delArray = evt.GetInvocationList();
for(int i=0; i<delArray.Length; i++)
{
Delegate ev = delArray;
Trace.WriteLine(ev.Method);
Trace.WriteLine(ev.Target);
}


Thanks, but that doesn't get it either.

Given evt.GetInvocationList() -- how does one do this
dynamically, at runtime. You knew, at compile time, that
it's "evt". I don't know this.

I know there's an event named "evt" -- I got that via the
Reflection mentioned in my original post. How do I,
at run time, do something like:

this.("evt").GetInvocationList

or, more to my question:

EventInfo[] events = this.GetType().GetEvents( BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic );

for (int i = 0; i < events.Length; i++)
{

// This is the line (below)

this.(events.Name).GetInvocationList //wrong!!

// how do I get the InvocationList, knowing that I've got the name
// of the event in events.Name ???

}
 
Sgt. Sausage said:
mdb said:
You can't in all cases. In some cases it's relatively straightforward.
If the event has been defined in C# as:

public event Foo;

Assuming that the event is formed in a normal manner, though, you can
access the Method and Target parameters of the event delegates...

public event Event_Handler evt;
Delegate[] delArray = evt.GetInvocationList();
for(int i=0; i<delArray.Length; i++)
{
Delegate ev = delArray;
Trace.WriteLine(ev.Method);
Trace.WriteLine(ev.Target);
}


Thanks, but that doesn't get it either.

Given evt.GetInvocationList() -- how does one do this
dynamically, at runtime. You knew, at compile time, that
it's "evt". I don't know this.

I know there's an event named "evt" -- I got that via the
Reflection mentioned in my original post. How do I,
at run time, do something like:

this.("evt").GetInvocationList

or, more to my question:

EventInfo[] events = this.GetType().GetEvents( BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic );

for (int i = 0; i < events.Length; i++)
{

// This is the line (below)

this.(events.Name).GetInvocationList //wrong!!

// how do I get the InvocationList, knowing that I've got the name
// of the event in events.Name ???

}


Hate to reply to my own post, but on another forum I found this in the
archives:

Recently I wanted to test whether we were successfully releasing
all our event subscriptions when an object was disposed. I thought
this would be a simple task, use Type.GetEvents() to get the events
and then use the resulting EventInfo's to get a list of subscribers. Not
quite so simple, while EventInfo has methods to add and remove events
, it has no invocation list. After a bit more work I discovered the
trick
is to ask for the events as fields and then cast the resulting object to
Delegate. Now I can invoke GetInvocationList(). Too bad MS didn't
include GetInvocationList() on the EventInfo. Problem solved: our unit
tests are starting to get vey good at catching subscriber leaks.

The thread is old, and long since dead.

If anyone knows the actual code to do as described above, I'd
like to see it.

Thanks.
 
Sgt. Sausage said:
Hate to reply to my own post, but on another forum I found this in the
archives:

Recently I wanted to test whether we were successfully releasing
all our event subscriptions when an object was disposed. I thought
this would be a simple task, use Type.GetEvents() to get the events
and then use the resulting EventInfo's to get a list of subscribers. Not
quite so simple, while EventInfo has methods to add and remove events
, it has no invocation list. After a bit more work I discovered the
trick
is to ask for the events as fields and then cast the resulting object to
Delegate. Now I can invoke GetInvocationList(). Too bad MS didn't
include GetInvocationList() on the EventInfo. Problem solved: our unit
tests are starting to get vey good at catching subscriber leaks.

The thread is old, and long since dead.

If anyone knows the actual code to do as described above, I'd
like to see it.

Thanks.

Again with the replying to my own post -- figured it out.
 
Back
Top