Maintain list of attached event handlers (.Net 1.1)

A

Armin Zingler

Hi,

here's the short version:
How can I remember which event handlers did I attach to which events?

Background:
- I have an object tree. There is one root object, containing sub objects,
containing sub objects, and so on. All the objects in the tree can raise
events.

- In a Form's Load event, I recursively process the object tree and attach
event handlers to all the events of the objects in the tree.

- Whenever an event fires, I detach the event handler. (because it won't
fire twice)

- When the Form closes, maybe not all events have been fired. Therefore,
there are still event handlers attached to these events.

My goal is:
I must detach the remaining event handlers when closing the Form.

Question:
How do I know which event handlers are still attached? I thought I simply
could maintain a list of attached event handlers. Whenever an event fires,
I detach the event handler and remove the corresponding entry from the
list. So, if the Form closes, I only have to process the list in order to
detach the remaining handlers.

But, /what/ do I have to store in the list? I didn't find a way without
using Reflection.

Thanks for reading!


Armin
 
P

Peter Duniho

[...]
- In a Form's Load event, I recursively process the object tree and
attach
event handlers to all the events of the objects in the tree.

- Whenever an event fires, I detach the event handler. (because it won't
fire twice)

- When the Form closes, maybe not all events have been fired. Therefore,
there are still event handlers attached to these events.

My goal is:
I must detach the remaining event handlers when closing the Form.

I don't see any need to detach the event handler regardless. It is not
really hurting anything for it to remain attached even after the event
fires, other than a very minimal amount of memory occupied. And if you
are releasing the object itself, any attached event handlers will also be
released at that time, without you doing anything extra.

Why is it that you think you need to detach the event handlers?

Pete
 
A

Armin Zingler

Peter Duniho said:
[...]
- In a Form's Load event, I recursively process the object tree
and attach
event handlers to all the events of the objects in the tree.

- Whenever an event fires, I detach the event handler. (because it
won't fire twice)

- When the Form closes, maybe not all events have been fired.
Therefore, there are still event handlers attached to these
events.

My goal is:
I must detach the remaining event handlers when closing the Form.

I don't see any need to detach the event handler regardless. It is
not really hurting anything for it to remain attached even after the
event fires, other than a very minimal amount of memory occupied. And if
you are releasing the object itself, any attached event
handlers will also be released at that time, without you doing
anything extra.

Why is it that you think you need to detach the event handlers?

The Form is an observer of some objects working in the background in another
thread. After the Form has closed, it doesn't have to observe the events
anymore. In the event handlers, I call Me.BeginInvoke to marshal the work to
the UI thread, but as there is no Form, there is no need to do this anymore.
Of course, I could always query "IsHandleCreated" in each event handler, but
I think it's straighter to detach the event handlers when closing the Form.


Armin
 
P

Peter Duniho

The Form is an observer of some objects working in the background in
another thread. After the Form has closed, it doesn't have to observe
the events anymore. In the event handlers, I call Me.BeginInvoke to
marshal the work to the UI thread, but as there is no Form, there is no
need to do this anymore. Of course, I could always query
"IsHandleCreated" in each event handler, but I think it's straighter to
detach the event handlers when closing the Form.

Ah, I misunderstood. Sorry. Your original wording made it sound as
though it was the objects owned by the form itself that exposed the
event. Just to clarify: the form itself does not actually contain the
tree. The tree is only in some data structure, and form subscribes to an
event (or events) on each and every object within the tree. And yes, I
agree that if the form is going away, it should unsubscribe itself from
any events to which it's subscribed.

It is not a problem to unsubscribe from an event that you are not
subscribed to. So one option is to simply go through an unsubscribe from
every event when the form unloads, just as you go through and subscribe to
every event when the form loads.

I do not believe there is any point in unsubscribing when the event fires,
even if the event will never be raised again. So, if you agree with that
then you can simply do as I suggest above (unsubscribe to every event when
the form unloads) _without_ bothering to unsubscribe any other time.

Pete
 
J

John Saunders [MVP]

Armin Zingler said:
Hi,

here's the short version:
How can I remember which event handlers did I attach to which events?

Background:
- I have an object tree. There is one root object, containing sub objects,
containing sub objects, and so on. All the objects in the tree can raise
events.

- In a Form's Load event, I recursively process the object tree and attach
event handlers to all the events of the objects in the tree.

- Whenever an event fires, I detach the event handler. (because it won't
fire twice)

Why detach the handler right away? Why not wait until the form closes, then
detach them all? Follow the same (visitor) pattern you followed to attach
the handlers to detach them.
 
A

Armin Zingler

Peter Duniho said:
Ah, I misunderstood. Sorry. Your original wording made it sound as
though it was the objects owned by the form itself that exposed the
event. Just to clarify: the form itself does not actually contain
the tree. The tree is only in some data structure, and form
subscribes to an event (or events) on each and every object within
the tree.

Correct. In other words, the tree can live without the Form.
And yes, I agree that if the form is going away, it
should unsubscribe itself from any events to which it's subscribed.

It is not a problem to unsubscribe from an event that you are not
subscribed to. So one option is to simply go through an unsubscribe
from every event when the form unloads, just as you go through and
subscribe to every event when the form loads.

I do not believe there is any point in unsubscribing when the event
fires, even if the event will never be raised again. So, if you
agree with that then you can simply do as I suggest above
(unsubscribe to every event when the form unloads) _without_
bothering to unsubscribe any other time.

Pete

You're right, I could do this. Though, isn't it interesting that it is not
possible to store the information I need to remember? I only want to
remember "I attached this handler to that event". I am able to remember
"this handler" because it's just a Delegate, but I am not able to rember
"that event" (without using reflection).

I can not belive that it is not possible.



Armin
 
P

Peter Duniho

[...]
You're right, I could do this. Though, isn't it interesting that it is
not possible to store the information I need to remember? I only want to
remember "I attached this handler to that event". I am able to remember
"this handler" because it's just a Delegate, but I am not able to rember
"that event" (without using reflection). I can not belive that it is not
possible.

It is possible. You don't even need reflection. There's just no point in
it.

Pete
 
A

Armin Zingler

John Saunders said:
Why detach the handler right away? Why not wait until the form
closes, then detach them all? Follow the same (visitor) pattern you
followed to attach the handlers to detach them.

Why /not/ detach them right away? :) I know, because then I would not have
the problem, but in general it is not a fault to detach them immediatelly.
Though, the question remains how I can store the information that "I
attached this handler to that event".

Background:
The tree mentioned describes an execution plan in another thread. Each node
in the tree represents a piece of work. A node contains status information
and fires "Started" and "Done" events. Whenever I receive the "Started" or
"Finished" event, I unsubcribed from it because it will never fire again.
Right, I am not forced to unsubscribe immediatelly, but IMO it's more
correct than wrong. In the end, when closing the Form, I want to unsubsribe
to all remaining handlers that are still attached.

Though, it is still interesting for me that I can not store the information
in a list. Actually each item would be a pair of two objects: One pointing
to the event (type: Multicastdelegate), the other one pointing to the event
handler (Delegate).

Below[1] I inserted the real-world code that I had to write due to a lack of
solution that I am looking for. You don't have to read it because it's
lengthier. It shows that I currently have to handle each event individually.
It was a lot to type even though there were only two events. In other
situations there might be more, that's why I was looking for a general
solution.

Finally, all events are based on the same concept, so polymorphism should be
appliable here.



Armin

[1]
Private Class HookedStartedEvent
Public handler As CodeBlockInstanceStatus.StartedEventHandler
Public obj As CodeBlockInstanceStatus

Public Sub New( _
ByVal handler As CodeBlockInstanceStatus.StartedEventHandler, _
ByVal obj As CodeBlockInstanceStatus)

Me.handler = handler
Me.obj = obj
End Sub

Public Sub Remove()
RemoveHandler obj.Started, handler
End Sub
End Class

Private Class HookedFinishedEvent
Public handler As CodeBlockInstanceStatus.FinishedEventHandler
Public obj As CodeBlockInstanceStatus

Public Sub New( _
ByVal handler As CodeBlockInstanceStatus.FinishedEventHandler, _
ByVal obj As CodeBlockInstanceStatus)

Me.handler = handler
Me.obj = obj
End Sub
Public Sub Remove()
RemoveHandler obj.Finished, handler
End Sub
End Class


Private f_HookedStartedEvents As New ArrayList
Private f_HookedFinishedEvents As New ArrayList

Private Sub OnCodeBlockStarted(ByVal sender As Object)

If Me.InvokeRequired Then
BeginInvoke( _
New CodeBlockInstanceStatus.StartedEventHandler(AddressOf
OnCodeBlockStarted), _
New Object() {sender} _
)
Else
Dim Status As CodeBlockInstanceStatus

Status = DirectCast(sender, CodeBlockInstanceStatus)

RemoveHandler Status.Started, AddressOf OnCodeBlockStarted

For Each item As HookedStartedEvent In f_HookedStartedEvents
If item.obj Is Status Then
item.Remove()
f_HookedStartedEvents.Remove(item)
Exit For
End If
Next

End If

End Sub

Private Sub ThreadWatcher_HandleDestroyed( _
ByVal sender As Object, ByVal e As System.EventArgs) _
Handles MyBase.HandleDestroyed

For Each item As HookedFinishedEvent In f_HookedFinishedEvents
item.Remove
Next

For Each item As HookedStartedEvent In f_HookedStartedEvents
item.Remove
Next

f_HookedFinishedEvents = Nothing
f_HookedStartedEvents = Nothing
End Sub
 
A

Armin Zingler

Peter Duniho said:
[...]
You're right, I could do this. Though, isn't it interesting that
it is not possible to store the information I need to remember? I
only want to remember "I attached this handler to that event". I
am able to remember "this handler" because it's just a Delegate,
but I am not able to rember "that event" (without using
reflection). I can not belive that it is not possible.

It is possible. You don't even need reflection. There's just no
point in it.

As I am not able to store a reference to an event, I don't see how it could
be possible. There is a point in it because that's the situation. Maybe it
won't lead to an exception if I detach from an event that I have already
detached from, but I would like to detach only from those events that I am
still handling. Therefore I need a list. I'm looking forward to a way
wihtout reflection.


Armin
 
P

Peter Duniho

Why /not/ detach them right away? :) I know, because then I would not
have
the problem,

No. That's not true. The question of being able to unsubscribe event
handlers later has nothing to do with whether you also unsubscribe as they
are raised. I personally think unsubscribing is a waste of time, but it
doesn't affect the more general issue. So, you should stop worrying about
it. If you want to unsubscribe, fine. It's not causing a problem.
but in general it is not a fault to detach them immediatelly.
Though, the question remains how I can store the information that "I
attached this handler to that event".

Why do you need this information? You obviously can enumerate all of the
objects for which you want to subscribe to an event or events. So, do the
same enumeration when you want to unsubscribe, and unsubscribe from every
event of every object that you subscribed to in the first place.

For some objects, this operation will do nothing, because you've already
unsubscribed. But that's not a problem.
Background:
The tree mentioned describes an execution plan in another thread. Each
node
in the tree represents a piece of work. A node contains status
information
and fires "Started" and "Done" events. Whenever I receive the "Started"
or
"Finished" event, I unsubcribed from it because it will never fire again.
Right, I am not forced to unsubscribe immediatelly, but IMO it's more
correct than wrong.

I disagree, but you are writing the code, so obviously your opinion
carries more weight. Regardless, as I mentioned this question has nothing
to do with how you resolve the unsubscription at the end.
In the end, when closing the Form, I want to unsubsribe
to all remaining handlers that are still attached.

So do that. Traverse your tree and unsubscribe from all the events, just
as you traversed the tree and subscribed to start with.
Though, it is still interesting for me that I can not store the
information
in a list. Actually each item would be a pair of two objects: One
pointing
to the event (type: Multicastdelegate), the other one pointing to the
event
handler (Delegate).

In C#, there are no references to references. This is a fundamental
limitation, granted. But it doesn't prevent things from being done. It
just changes how they need to be done.

In C, you might do something like this (if C allowed this syntax, that is):

public event MyHandlerType myevent;

and then later do this:

MyHandlerType *pevent = &myevent;
*pevent += MyEventHandler;

so that even later you could do this:

*pevent -= MyEventHandler;

That's not possible in C#. However, that doesn't mean that you can't
instead retain a reference to the object with the event itself, and gain
access to the event later that way.

So instead, you'd write something like this:

class MyEventRaisingClass
{
public event MyHandlerType myevent;
...
}

and then later do this:

MyEventRaisingClass raiser = ...;

raiser.myevent += MyEventHandler;

and then even later do this:

raiser.myevent -= MyEventHandler;

The two are basically equivalent. The only difference is that in one case
you know only the event instance, while in the other case you have the
reference to the entire containing class instance.
Below[1] I inserted the real-world code that I had to write due to a
lack of solution that I am looking for. You don't have to read it
because it's lengthier.

As near as I can tell, you are doing what I described above, in that you
retain a reference to the object containing the event rather than to the
event itself. Frankly, I don't know why you think that this is worse than
having a reference to the event. As I mentioned, it is basically
equivalent.

There are, however, some things that don't make sense to me in your code..

It doesn't actually explain the issue you're asking about, because you
don't appear to have included all of the code that does stuff. In
particular, I don't see any of the setup, nor is clear how events are
actually raised. But beyond that, you appear to be removing the same
handler twice. Once in the event handling method itself:

RemoveHandler Status.Started, AddressOf OnCodeBlockStarted

And then again in the Remove() method of a class the purpose for which I
have yet to determine:

Public Sub Remove()
RemoveHandler obj.Started, handler
End Sub

In addition, every time an event handler is called, you enumerate a list
of all known subscribed event handlers (I think...again, you left out
details that would clarify this). In what way is that better than simply
enumerating all of the objects when the form is finally unloaded and
unsubscribing from each object there? It certainly results in a lot more
execution of code (it's essentially an O(N^2) algorithm, as opposed to the
O(N) algorithm that both John and I have proposed to you.
It shows that I currently have to handle each event individually. It was
a lot to type even though there were only two events.

Not that I think that the method that it appears you are using is all that
great anyway, but I don't see how typing 13 lines for a class specific to
the event is really all that big of a deal.

Pete
 
P

Peter Duniho

As I am not able to store a reference to an event, I don't see how it
could
be possible.

See my other post. You simply retain a reference to the object containing
the event, rather than to the event itself.
There is a point in it because that's the situation.

Giving you the benefit of the doubt, maybe "that is the situation".
However, so far you haven't posted anything that would verify that
statement.
Maybe it won't lead to an exception if I detach from an event that I
have already detached from, but I would like to detach only from those
events that I am still handling.

Why? Of what concern is it to you? Why are you insisting on doing extra
work to avoid something that is perfectly legal and efficient?
Therefore I need a list. I'm looking forward to a way wihtout reflection.

You can do the list. You can do it without reflection (in fact, you seem
to have already done it without reflection). But even so, there's no
point to it. All you've done is make your code slower. It's
functionality is the same as if you had just unsubscribed all the events
at the end, and you do more work to accomplish the same thing.

I suppose the phrase "no point to it" is open to interpretation. But as I
see it, there's no point in doing what you seem to be asking to do.

Pete
 
P

Peter Duniho

[...]
It shows that I currently have to handle each event individually. It
was a lot to type even though there were only two events.

Not that I think that the method that it appears you are using is all
that great anyway, but I don't see how typing 13 lines for a class
specific to the event is really all that big of a deal.

And as a clarification for this point:

Even if you feel you must maintain a list of subscribed events, you do not
need a new class for every event type. All you need to keep is a list of
the objects for which you've subscribed; all the other information is
already known at compile time and can be referred to explicitly when you
want to unsubscribe.

I don't think the typing should be an issue anyway, but it's not really
required. So if you object to it, don't do it.

Pete
 
A

Armin Zingler

Peter Duniho said:
Why? Of what concern is it to you? Why are you insisting on doing
extra work to avoid something that is perfectly legal and efficient?

Why do you insist on doing extra work by detaching event handlers that have
already been detached before? Why keep listening to events that will never
occur?

See other post for more.



Armin
 
A

Armin Zingler

Peter Duniho said:
For some objects, this operation will do nothing, because you've
already unsubscribed. But that's not a problem.

To make it a bit shorter, I'll try to resume:
You twice say that it is not a problem (to immediatelly unsubscribe). So, we
can say that it is up to me. But if I have the choice between either listen
to an event that will never occur, or don't listen to it anymore, the latter
obviously makes more sense. That's the reason why I unsubscribe
immediatelly.
In C#, there are no references to references.

Why "references to references"? (I'm using VB.Net BTW) A reference to a
MulticastDelegate object is possible. The event is a MulticastDelegate
object. My only problem is that it is private in the class.
[Meanwhie I know what you might be referring to but I'm not sure yet; see my
example and the explanation at the end]

Sorry, I can not comment the C-Code.
[C#]
[...]
Below[1] I inserted the real-world code that I had to write due to
a lack of solution that I am looking for. You don't have to read
it because it's lengthier.

As near as I can tell, you are doing what I described above, in that
you retain a reference to the object containing the event rather
than to the event itself. Frankly, I don't know why you think that
this is worse than having a reference to the event.

Because it's more work. If I have to reference the object, I will have to
handle different
object types and different events individually. I just want a simple loop:

for each item in mylist
removehandler ...
-or-
[delegate].Remove ...
next

Sort of p-Code, but that's all I am looking for.

There are, however, some things that don't make sense to me in your
code.

Sorry, but everything makes sense and works well.
It doesn't actually explain the issue you're asking about, because
you don't appear to have included all of the code that does stuff. In
particular, I don't see any of the setup, nor is clear how events
are actually raised.

Yes, I did not include the whole code. The code explains well how I have to
handle the different events individually because the code is not based on a
base class (like MultiCastDelegate) that is common to all events, no matter
which object and no matter which event.

But beyond that, you appear to be removing the
same handler twice. Once in the event handling method itself:

RemoveHandler Status.Started, AddressOf OnCodeBlockStarted

And then again in the Remove() method of a class the purpose for
which I have yet to determine:

Public Sub Remove()
RemoveHandler obj.Started, handler
End Sub

In addition, every time an event handler is called, you enumerate a
list of all known subscribed event handlers (I think...again, you
left out details that would clarify this). In what way is that
better than simply enumerating all of the objects when the form is
finally unloaded and unsubscribing from each object there? It
certainly results in a lot more execution of code (it's essentially
an O(N^2) algorithm, as opposed to the O(N) algorithm that both John
and I have proposed to you.

I don't remove anything twice. After removing the handler, I remove the item
from the list. If it's not in the list anymore, it won't be removed again
when the Form is destroyed.

You are right, the loop to find the item in the list was a quicky. Could be
a hashtable or whatever. But it doesn't matter. If you want to remove
something from a list, it has to be done.
Not that I think that the method that it appears you are using is
all that great anyway, but I don't see how typing 13 lines for a
class specific to the event is really all that big of a deal.

Compared to

class Item
event as multitaskdelegate
eventhandler as delegate
end class

it is pretty much to type, and in real-world there are some more events, as
a consequence some more "Class HookedXYZEvent", and some more arraylist and
some more loops to process the arraylists. If I could keep it all in one
list (of course, without different types of list items), it would be simple
in one go.



[Later....]
An example how it (almost) works. Comments see below.


Public Class Form1

Class C1
Public Event Progress(ByVal p As Integer)
Public ReadOnly Property MCD() As MulticastDelegate
Get
Return ProgressEvent
End Get
End Property
Public Sub Raise()
RaiseEvent Progress(0)
End Sub
End Class

Class C2
Public Event Done()
Public ReadOnly Property MCD() As MulticastDelegate
Get
Return DoneEvent
End Get
End Property
Public Sub Raise()
RaiseEvent Done()
End Sub
End Class

Private Class Item
Public [event] As MulticastDelegate
Public Handler As [Delegate]

Public Sub New(ByVal [event] As MulticastDelegate, _
ByVal Handler As [Delegate])

Me.event = [event]
Me.Handler = Handler
End Sub
End Class

Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load

Dim al As New List(Of Item)
Dim o1 As New C1
Dim o2 As New C2

Dim d1 As New C1.ProgressEventHandler(AddressOf OnProgress)
Dim d2 As New C2.DoneEventHandler(AddressOf OnDone)

'add the event handlers...
AddHandler o1.Progress, d1
AddHandler o2.Done, d2

'...and remember which events I handle.
al.Add(New Item(o1.MCD, d1))
al.Add(New Item(o2.MCD, d2))

o1.Raise()
o2.Raise()

'...
'later: Remove all event handlers

For Each i As Item In al
[Delegate].Remove(i.event, i.Handler)
Next

o1.Raise()
o2.Raise()

End Sub
Private Sub OnProgress(ByVal p As Integer)
MsgBox("OnProgress")
End Sub
Private Sub OnDone()
MsgBox("OnDone")
End Sub

End Class


Comments:
- You see that the MCD properties make the internal MCDs public.
- This enables me storing a reference on them externally.
- This is usually not done, but only in this example. At least it is not
possible with classes authored by somebody else.
- The main point: In the loop that removes the handles, I don't have to care
about the object types and the events. All in one go! In addition: I don't
have to write one Item class for each event I want to handle.

Though, why I wrote it "almost" works: What I did not know until know is
that the invocation list of a MultiCastDelegate object (MCD) seems to be
readonly. I can only remove one item from the invocation list by creating a
new MCD. As a consequence, I would have to store the new MCD back in the
object - which is obviously not an option and not possible at all for
foreign classes.

Bottom line, the example shows how to store the information that I want to
store: I want to remember that I added /this/ handler to /that/ event
without individual handling different objects and events because the
event concept is the same for all of them. I also think that the example
makes clear what was my intention, and that it is - almost - possible. :)


Please don't get me wrong: I use .Net since the first public SDK preview (in
Y2K?), and I am a programmer for 20 years now, so I (usually ;-) ) know what
I do. I don't insist on anything, but I thought that events have a common
concept, common base clases, and therefore it's not absurd to
think..uhhm..what I think. Now it turned out that under the hood called
"Events" there is something going on that makes it impossible to work as
intended.

Anyways, thanks a lot for your time and trying to help!



Armin
 
P

Peter Duniho

Why do you insist on doing extra work by detaching event handlers that
have
already been detached before?

It's not extra work. It's _less_ work.
Why keep listening to events that will never occur?

Having a delegate attachd to an event is not "listening". It incurs zero
performance penalty. Zero. If the memory cost is unacceptable after the
event has been raised, then it was unacceptable before it was raised. The
question of removing the delegate or not is a red herring. And leaving
the delegate subscribed does not involve any execution overhead whatsoever.
 
P

Peter Duniho

You twice say that it is not a problem (to immediatelly unsubscribe).
So, we can say that it is up to me. [...]

That's fine. My point is that you should stop bringing it up. Your
requirement to unsubscribe when the event fires is completely independent
of how you unsubscribe other events later. The implementation of one has
no effect on the implementation of the other.

So, please. Stop bringing it up. It only muddies the waters.
[...]
Why "references to references"? (I'm using VB.Net BTW) A reference to a
MulticastDelegate object is possible. The event is a MulticastDelegate
object.

A basic rule of event management is that when you subscribe the first
handler, the reference to the event has to be instantiated. Likewise,
when you unsubscribe the last hander, the reference has to be set to
null. Only if you have access to that reference can this be done. Having
a reference to the object itself is useless.

My inference was that this inability to modify the event reference itself
was the crux of your concern. But perhaps I was wrong. It doesn't
matter. You are still making the task much harder than it needs to be.
(An alternative explanation is that you are not describing your problem
correctly...I don't know. Only through discussing it will you be able to
figure that out and clarify what you mean).
As near as I can tell, you are doing what I described above, in that
you retain a reference to the object containing the event rather
than to the event itself. Frankly, I don't know why you think that
this is worse than having a reference to the event.

Because it's more work. If I have to reference the object, I will haveto
handle different object types and different events individually. I just
want
a simple loop:

for each item in mylist
removehandler ...
-or-
[delegate].Remove ...
next

So write the simple loop. Keep one list for each event that you might
subscribe to. For each list, you know what the event is, and you know
what the handler delegate is. You can easily write those explicitly.
Assuming your object is type MyObj, the event for the list named lmyobj1
is MyObj.MyEvent, and you are using the method MyHandler for the event,
you'd just enumerate the list like this:

foreach (MyObj myobj in lmyobj1)
{
myobj.MyEvent -= MyHandler;
}

No need for all the extra classes you're writing.
Sorry, but everything makes sense and works well.

That don't make sense TO ME. Whether it makes sense to you is
irrelevant. If you want help, you need to make sure that things make
sense to the people trying to help.

As for "works well"...if it works well, why are we here? Why not just use
that code?
I don't remove anything twice. After removing the handler, I remove the
item
from the list. If it's not in the list anymore, it won't be removed again
when the Form is destroyed.

You do remove the event handler twice. This is the code you posted:

RemoveHandler Status.Started, AddressOf OnCodeBlockStarted

For Each item As HookedStartedEvent In f_HookedStartedEvents
If item.obj Is Status Then
item.Remove()
f_HookedStartedEvents.Remove(item)
Exit For
End If
Next

You'll notice that in the HookedStartedEvents, the Remove() method looks
like this:

Public Sub Remove()
RemoveHandler obj.Started, handler
End Sub

You execute this method when item.obj == Status, which is the obejctfrom
which you just removed the handler. In other words, first you call
RemoveHandler to remove the handler from Status.Started, then you call
item.Remove() which does the exact same thing.
You are right, the loop to find the item in the list was a quicky. Could
be
a hashtable or whatever. But it doesn't matter. If you want to remove
something from a list, it has to be done.

You would prefer to add a hashtable to your program, with all of the
memory overhead that requires, than simply allow a delegate reference to
stick around after you know it won't be used?

Ever hear the phrase "penny-wise, pound-foolish"?
Compared to

class Item
event as multitaskdelegate
eventhandler as delegate
end class

it is pretty much to type,

It seems to me that you are getting stuck here. You need to forget the
possibility of maintaining some general-purpose reference to the event.
It's not possible, and not needed. As long as you continue to view this
problem only through that narrow view, you will continue to miss the
forest for the trees.

You need to think "outside the box" that you have created for yourself,
and see that the basic mechanism by which you are intent on solving the
issue is not required and is leading you to want to do things that .NET
simply doesn't allow.
and in real-world there are some more events, as
a consequence some more "Class HookedXYZEvent", and some more arraylist
and
some more loops to process the arraylists. If I could keep it all in one
list (of course, without different types of list items), it would be
simple
in one go.

Again:

You say you already have an enumeration of your tree that subscribes to
the events. While you didn't actually post any code that showed such an
enumeration, I will take as granted that somewhere you actually do.

The solution here is to simply repeat the same enumeration at the point in
time that you want to unsubscribe the events still subscribed (when the
form unloads or closes or whatever). Forget about whether you've already
also unsubscribed events that you know won't be raised. That doesn't
matter. You can do that if you like, it doesn't change the solution for
dealing with the form closing case.

Just enumerate all your objects, and unsubscribe the same event handlers
that you subscribed at the start. It's simple, it works, and is MORE
performant than trying to maintain a list or lists or other data
structures as various events are raised and unsubscribed from.
[...]
Comments:
- You see that the MCD properties make the internal MCDs public.

Sort of. You don't have access to the actual event. What you get is a
copy of the event. It's just like assigning the event to a local variable
in a function. You don't get a reference to the original multicast
delegate; you get a complete copy. If the multicast delegate changes
later, the copied reference does not change.
- This enables me storing a reference on them externally.

No, it doesn't. It enables you to store a copy of the multicast delegate
externally.
- This is usually not done, but only in this example. At least it is not
possible with classes authored by somebody else.
- The main point: In the loop that removes the handles, I don't have to
care
about the object types and the events. All in one go!

You didn't show the enumeration to initialize the handlers. However, the
unsubscribing is not different from the subscribing. So if it's okay to
write the code to subscribe, it should be fine to write the same code to
unsubscribe.
In addition: I don't
have to write one Item class for each event I want to handle.

That's right, you don't. You wouldn't if you followed the advice that
both John and I have offered as well.
Though, why I wrote it "almost" works: What I did not know until know is
that the invocation list of a MultiCastDelegate object (MCD) seems to be
readonly. I can only remove one item from the invocation list by
creating a
new MCD. As a consequence, I would have to store the new MCD back in the
object - which is obviously not an option and not possible at all for
foreign classes.

Exactly. That's my point. What you're trying to do is not supported by
the framework.
Bottom line, the example shows how to store the information that I want
to
store: I want to remember that I added /this/ handler to /that/ event
without individual handling different objects and events because the
event concept is the same for all of them. I also think that the example
makes clear what was my intention, and that it is - almost - possible.
:)

Let me see if I can put the problem another way:

You have stumbled across what you believe to be a nail. Because of this,
you built a hammer that you want to apply to the nail. No matter what I
or John or anyone else say, you insist on using the hammer. Even though
it turns out that you don't have a nail at all. You've got a screw, and
it can be dealt with more effectively using a more appropriate tool thana
hammer.
[...] Now it turned out that under the hood called
"Events" there is something going on that makes it impossible to work as
intended.

As intended by whom? You? Yes, that seems to be true. It appears to be
impossible to do what you seem to be dead-set on doing.

The rest of us? Not so much. We take what we know about events, and
apply that knowledge in a different way, coming up with solutions that are
efficient and work _with_ the existing architecture of events, rather than
fighting it.

Just enumerate your tree when the form unloads, unsubscribing all the
events you subscribed when the form loaded. You'll be much happier, your
code will work fine, and you won't have to add a whole bunch of new
classes, one for each event.

Try it, you'll like it!

Pete
 
A

Armin Zingler

Peter Duniho said:
It's not extra work. It's _less_ work.

I see, doing something twice is less work...? Ok. You should distinguish
between programming work and work done by the program at runtime. I'm
referring to uselessly detaching the same event handler twice at runtime. I
do accept the additional programming work to avoid this superfluous step
whereas you don't. There's nothing more I can add, not now and not in
future posts.
Having a delegate attachd to an event is not "listening".

Use the word of your choice, but I call it listening.
It incurs
zero performance penalty. Zero. If the memory cost is unacceptable
after the event has been raised, then it was unacceptable before it
was raised. The question of removing the delegate or not is a red
herring. And leaving the delegate subscribed does not involve any
execution overhead whatsoever.

I have never mentioned performance or memory costs. That was /you/. So, how
can it be /my/ problem? Ok, the very last time: You accept a situation that
doesn't hurt. I don't accept a situation that doesn't make sense. Having
event handlers attached to events that will never fire does not make sense
(TO ME). Sorry, I forgot you don't want to discuss this anymore. But me too,
so, please, let's stop at this point.


Armin
 
A

Armin Zingler

Peter Duniho said:

Now, based on my own researches, that I found out that it is not possible to
do what I was trying, I'm closing this discussion for my part. I would have
to repeat myself again and again. I don't like that you are inappropriatly
speaking tartly. Blame me for not knowing the deepest details of how events
internally work. If that's what you are after....


BTW, my quoted conclusion in the VB.Net group:

"The problem is what's going on under the hood when adding/removing event
handlers: The AddHandler keyword doesn't expect an object as the first
argument, but it's the /name/ of an event. If it was an object, it would be
possible to store a reference. Everything done internally by executing
Addhandler is specific to the class and to the event. As it's specific,
there is no multipurpose solution that enables me storing references to
events in the way intended."


Armin
 
P

Peter Duniho

I see, doing something twice is less work...?

Yes. It's less work than doing something once, maintaining a list on an
on-going basis, and then still having to traverse the list later anyway.
Ok. You should distinguish
between programming work and work done by the program at runtime.

Okay, to be clear: I am talking about work done by the program at runtime.
I'm
referring to uselessly detaching the same event handler twice at runtime.

It's true, some times that operation will be unnecessary. However, that
doesn't mean it's more work than the alternative.
I do accept the additional programming work to avoid this superfluous
step
whereas you don't.

I don't? I don't what? Accept additional programming work? That's
absurd. You have no idea what I do or do not accept, nor am I talking
about programming work.
Use the word of your choice, but I call it listening.

And you called it "catching" too. That doesn't make it right. More
particularly, the word "listen" implies some sort of active role. You can
use the word "listen" if you like, and we will even understand you. My
point is to be sure you understand that there is no actual active
listening going on. It hurts nothing to have a delegate attached to an
event, as that involves no actual run time code execution.
I have never mentioned performance or memory costs.

Of course you did. You are complaining about having to go through every
node in your tree at the end and unsubscribe, and your complaint is based
on your incorrect assertion that that's "more work". What is that, if not
a performance-based view?

I brought up memory as a way of explaining that I do understand the memory
costs involved in leaving the delegate attached. Since that's the _only_
cost to leaving it attached, I remain mystified as to why you care about
it remaining attached, since you "never mentioned memory costs". If you
don't care about memory costs, then why do you care about the delegate
remaining attached, given that memory costs are the _only_ costs involved
in doing so?
That was /you/. So, how
can it be /my/ problem? Ok, the very last time: You accept a situation
that
doesn't hurt. I don't accept a situation that doesn't make sense.

No, actually you don't accept a situation that DOES makes sense. It's
your proposal that makes no sense. You want to go to greater effort,
_both_ with respect to your time spent programming _and_ with respect to
the run-time code execution, to avoid a situation that isn't causing a
problem in the first place.
Having
event handlers attached to events that will never fire does not make
sense
(TO ME).

Then leave them there. You still don't need to maintain a list of the
handlers you have not yet removed.

Pete
 
A

Armin Zingler

[...]
And you called it "catching" too. That doesn't make it right. More
particularly, the word "listen" implies some sort of active role.

That's your interpretation. As I know what events are, I know what "listen
to an event" means. As you also know what events are, you should als know
what "listen to an event" means. I have used it in the past, unless when
teaching beginners, sufficient times without misinterpretation by the
reader.
You can use the word "listen" if you like, and we will even
understand you. My point is to be sure you understand that there is
no actual active listening going on.

You can be sure that I know this.
It hurts nothing to have a
delegate attached to an event, as that involves no actual run time
code execution.

Let me put it this way:
A basic programming rule that I (and not only I) always obey is: Clean up
references as soon as I don't need them anymore. There is no reason to
challenge this every time. Every time, I do not have to count the bytes
potentially freed, then decide if the amount legitimates writing the
removement of the reference. Instead, I simply remove it because I don't
need it anymore. That's the basis rule.
The same with the event handler: I don't need to handle the event anymore,
consequently I remove the handler. This is also part of a whole conecept
called "clean programming".

Of course you did. You are complaining about having to go through
every node in your tree at the end and unsubscribe, and your
complaint is based on your incorrect assertion that that's "more
work". What is that, if not a performance-based view?

As, in this case, I don't care whether it takes 1/100 or 1/1,000 of a
second, how can it be a performance issue? Again, /you/ mentioned
"performance" first. The result of your misinterpretation of why I don't
want to do superfluous things. It's not for performance reasons. It's just
illogical to do something that does not have to be done. (yes, I know "it
doesn't hurt" - but doing something that doesn't hurt but that is not
necessary is still illogical)
I brought up memory as a way of explaining that I do understand the
memory costs involved in leaving the delegate attached. Since
that's the _only_ cost to leaving it attached, I remain mystified as
to why you care about it remaining attached, since you "never
mentioned memory costs". If you don't care about memory costs, then
why do you care about the delegate remaining attached, given that
memory costs are the _only_ costs involved in doing so?

see above
No, actually you don't accept a situation that DOES makes sense.

I understand that you consider a handler, attached to an event that will
never be raised, makes sense. I don't have to repeat that this is not my
opinion.
It's your proposal that makes no sense. You want to go to greater
effort, _both_ with respect to your time spent programming _and_
with respect to the run-time code execution, to avoid a situation
that isn't causing a problem in the first place.


Then leave them there. You still don't need to maintain a list of
the handlers you have not yet removed.

see above


Well, I would like to be able to take a "snapshot" (metaphorically spoken.
no, it's not about debugging and how I want to do it, it's just a logical
snapshot) of the application at any (consistent) point in time and evaluate
the situation: If I looked at it and you had written the programm,
I would wonder why there are event handlers for events that will never fire.
I wouldn't consider it being a bug (because, we know, "it doesn't hurt"),
but I would consider it being illogical.

I think, everything is said about it, don't you, too? :)


Armin
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top