Re: Maintain list of attached event handlers (.Net 1.1) by Peter
Peter
Tue Jul 10 23:27:37 CDT 2007
On Tue, 10 Jul 2007 20:43:20 -0700, Armin Zingler <az.nospam@freenet.de>=
=
wrote:
> 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 independen=
t =
of how you unsubscribe other events later. The implementation of one ha=
s =
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. Havi=
ng =
a reference to the object itself is useless.
My inference was that this inability to modify the event reference itsel=
f =
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 t=
o =
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 have=
to
> handle different object types and different events individually. I jus=
t =
> 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 -=3D MyHandler;
}
No need for all the extra classes you're writing.
>> There are, however, some things that don't make sense to me in your
>> code.
>
> 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 u=
se =
that code?
> I don't remove anything twice. After removing the handler, I remove th=
e =
> item
> from the list. If it's not in the list anymore, it won't be removed ag=
ain
> 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 =3D=3D Status, which is the obejct=
from =
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. Cou=
ld =
> 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"?
>> > 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.
>
> 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 arraylis=
t =
> and
> some more loops to process the arraylists. If I could keep it all in o=
ne
> 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 alread=
y =
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 variab=
le =
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 delegat=
e =
externally.
> - This is usually not done, but only in this example. At least it is n=
ot
> possible with classes authored by somebody else.
> - The main point: In the loop that removes the handles, I don't have t=
o =
> care
> about the object types and the events. All in one go!
You didn't show the enumeration to initialize the handlers. However, th=
e =
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 t=
he
> 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 wan=
t =
> 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 examp=
le
> 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 than=
a =
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 b=
e =
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 a=
re =
efficient and work _with_ the existing architecture of events, rather th=
an =
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, you=
r =
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