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