.NET Forum / .NET Framework / New Users / July 2007
Maintain list of attached event handlers (.Net 1.1)
|
|
Thread rating:  |
Armin Zingler - 10 Jul 2007 21:40 GMT 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
Peter Duniho - 10 Jul 2007 22:11 GMT > [...] > - In a Form's Load event, I recursively process the object tree and [quoted text clipped - 9 lines] > 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
Armin Zingler - 10 Jul 2007 22:48 GMT > On Tue, 10 Jul 2007 13:40:17 -0700, Armin Zingler > <az.nospam@freenet.de> wrote: [quoted text clipped - 22 lines] > > 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
Peter Duniho - 10 Jul 2007 23:36 GMT > 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 [quoted text clipped - 3 lines] > "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
Armin Zingler - 11 Jul 2007 00:15 GMT > On Tue, 10 Jul 2007 14:48:20 -0700, Armin Zingler > <az.nospam@freenet.de> wrote: [quoted text clipped - 14 lines] > 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. [quoted text clipped - 11 lines] > > 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
Peter Duniho - 11 Jul 2007 00:50 GMT > [...] > You're right, I could do this. Though, isn't it interesting that it is [quoted text clipped - 3 lines] > "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
Armin Zingler - 11 Jul 2007 01:27 GMT > On Tue, 10 Jul 2007 16:15:14 -0700, Armin Zingler > <az.nospam@freenet.de> wrote: [quoted text clipped - 9 lines] > 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
Peter Duniho - 11 Jul 2007 01:58 GMT > 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
Armin Zingler - 11 Jul 2007 04:34 GMT > > 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? 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
Peter Duniho - 11 Jul 2007 04:53 GMT > 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.
Armin Zingler - 11 Jul 2007 12:12 GMT > On Tue, 10 Jul 2007 20:34:48 -0700, Armin Zingler > <az.nospam@freenet.de> wrote: [quoted text clipped - 4 lines] > > 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.
> > Why keep listening to events that will never occur? > > 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
Peter Duniho - 11 Jul 2007 22:21 GMT >> It's not extra work. It's _less_ work. > > 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.
>> > Why keep listening to events that will never occur? >> >> Having a delegate attachd to an event is not "listening". > > 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.
>> It incurs >> zero performance penalty. Zero. If the memory cost is unacceptable [quoted text clipped - 4 lines] > > 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
Armin Zingler - 12 Jul 2007 00:24 GMT [...]
> > > > Why keep listening to events that will never occur? > > > [quoted text clipped - 4 lines] > 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".
> > > It incurs > > > zero performance penalty. Zero. If the memory cost is [quoted text clipped - 9 lines] > 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 [quoted text clipped - 3 lines] > why do you care about the delegate remaining attached, given that > memory costs are the _only_ costs involved in doing so? see above
> > 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. 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_ [quoted text clipped - 8 lines] > 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
Peter Duniho - 12 Jul 2007 00:57 GMT > [...] > 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. Fine. Remove them then. It doesn't matter. I have said this several times. I do not know why you insist on dwelling on the issue.
> 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. I don't understand what the concern of "superfluous things" is if not as regards to performance. However, fine...I'll take as granted that this is a question of aesthetics and not performance. Your argument still fails:
> 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. But it _does_ have to be done. The alternative to is to do something else that otherwise does not have to be done.
You are arguing in favor of a more complicated implementation, so that you can avoid something that in theory doesn't have to be done. But the only scenario in which it doesn't have to be done is the one in which you complicate the implementation.
The complicated implementation "does not have to be done" any more than removing non-existent handlers "does not have to be done". So, how is it that you feel aesthetically it makes more sense to choose doing the thing that doesn't have to be done that is more complicated, than to choose the thing that doesn't have to be done that is simpler?
> (yes, I know "it > doesn't hurt" - but doing something that doesn't hurt but that is not > necessary is still illogical) It is only illogical if it's true that it's not necessary. It's necessary unless you are intent on doing something else that doesn't otherwise need to be done. I admit, in this context it appears to be unnecessary. But only because you are intent on doing something else that otherwise is unnecessary.
> [...] > I think, everything is said about it, don't you, too? :-) If I thought that "everything is said about it", I would stop saying things. As long as you continue to present illogical arguments, I will continue to point out the flaws in the logic.
Pete
Armin Zingler - 12 Jul 2007 03:25 GMT > On Wed, 11 Jul 2007 16:24:54 -0700, Armin Zingler > <az.nospam@freenet.de> wrote: [quoted text clipped - 7 lines] > several times. I do not know why you insist on dwelling on the > issue. I'm dwelling on it because you are again saying "it doesn't matter". Completely wrong. It does matter a lot because it is the precondition for the whole discussion.
> > 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/ [quoted text clipped - 3 lines] > not as regards to performance. However, fine...I'll take as granted > that this is a question of aesthetics and not performance. Glad to read this, though I'd call it logic not aesthetics.
> Your > argument still fails: [quoted text clipped - 5 lines] > > But it _does_ have to be done. Yes, but not twice. It /has/ already been done. The code has alrady been written in order to do it /before/. You are ingoring the preconditions. I explained why they are how they are. If you like to discuss under different preconditions, do it with whomever because it won't help /me/. A answer to a question that I did not ask, doesn't help.
> The alternative to is to do > something else that otherwise does not have to be done. > > You are arguing in favor of a more complicated implementation, so > that you can avoid something that in theory doesn't have to be done. Correct. Only "theory" must be replaced by "practice".
> But the only scenario in which it doesn't have to be done is the > one in which you complicate the implementation. [quoted text clipped - 5 lines] > complicated, than to choose the thing that doesn't have to be done > that is simpler? I'm afraid, I don't understand this sentence.
The whole situation can be expressed VB-ish: (no, not executable...)
If immediatelly remove handlers? then 'programmer's decision immediatelly remove handlers if do more than necessary? then 'programmer's decision do more than necessary 'means: detach some handlers a second 'time when closing the form '(aka illogical) else maintain a list of remaining handlers do only what's necessary 'means: only detach remaining handlers 'when closing the form (aka logical) end if else '... end if
- You are still trying to write the lower Else part, but the first If-expression has already been evaluated True. That's the precondition. The corresponding Else part is completely out of interest. Feel free to discuss it with somebody else but it's not what I am interested in.
- At the second If, you decide for True because you want to avoid the "maintain a list..." job, whereas I decide for the else block. I do accept the additional programming work ("maintain...") in order to "do only what's necessary", whereas you don't.
Our different decisions at the 2nd "If" are caused by differently weighting the pros and cons. For me, the True block is almost unacceptable whereas you accept it. To me it is almost unimportant how much work it takes to avoid an almost unacceptable situation. The additional reason to choose the Else block was pure technical interest in whether and how it is possible. Meanwhile I know that it is not possible the way I was looking for, nevertheless it is possible. Therefore I do it. If it wasn't possible at all, I would have to swallow the bitter pill and choose the True block.
So, we won't find an agreement because we have different weightings.
> > [...] > > I think, everything is said about it, don't you, too? :-) > > If I thought that "everything is said about it", I would stop saying > things. As long as you continue to present illogical arguments, I > will continue to point out the flaws in the logic. No problem. I will repeat my arguments until you got them and see that your's are illogical. I thought we are grown up enough to avoid this endless loop. Obviously you are not. Anyways, I won't have to add anything new.
Armin
Peter Duniho - 12 Jul 2007 05:13 GMT > I'm dwelling on it because you are again saying "it doesn't matter". > Completely wrong. It does matter a lot because it is the precondition for > the whole discussion. It's not. It never was. Removing the event handlers in the middle has no bearing on what the most efficient way to clean up at the end is.
> Glad to read this, though I'd call it logic not aesthetics. To make a logical presentation, you have to base it on something. Logic does not exist by itself. It requires axioms and operators. You haven't stated any axioms yet, so you cannot claim that it's "logic".
My axiomatic goals are performance and memory usage. You seem to think those are not relevant, and I have said that's fine. However, you haven't stated any other axiomatic goals on which you claim to base your decisions. I'm left with aesthetics, simply because that's a viable goal in code design at least some of the time and I have not guessed any other axiomatic goals you might have.
If you would like to state explicitly the axiomatic goals that guide you design, please feel free to do so. Please do NOT feel free to claim that your design is "logical" if you have not yet stated what axiomatic goals are used as the starting point for that "logical" conclusion.
>> But it _does_ have to be done. > > Yes, but not twice. It /has/ already been done. The code has alrady been > written in order to do it /before/. "Before" what? "It" what? Your posts would make a lot more sense if you would be more explicit about your nouns, instead of using pronouns. When you use pronouns in such an isolated context, I have to make some assumptions as to what they mean. You haven't declared a noun in advance of the pronoun, and you are unhappy with the assumptions I am making about your intent. So stop forcing me to make assumptions.
> You are ingoring the preconditions. I > explained why they are how they are. If you like to discuss under > different preconditions, do it with whomever because it won't help > /me/. A answer to a question that I did not ask, doesn't help. This all started with a very general, vague question about your overall goal. The only two replies you received both said essentially the same thing. If you wanted an answer to a different question, you should have been more clear about that.
> [...] >> The complicated implementation "does not have to be done" any more [quoted text clipped - 5 lines] > > I'm afraid, I don't understand this sentence. Which sentence don't you understand? Out of the three that you quoted, that is.
> The whole situation can be expressed VB-ish: (no, not executable...) > > If immediatelly remove handlers? then 'programmer's decision This is fine. It doesn't affect the rest of the discussion.
> immediatelly remove handlers Fine.
> if do more than necessary? then 'programmer's decision You will have to define "do more than necessary". By any obvious interpretation of the phrase, your conclusion does not result in not doing more than necessary.
> do more than necessary 'means: detach some handlers a second > 'time when closing the form > '(aka illogical) The cost of detaching some handlers a second time when closing the form is LOWER than the following clause:
> else > maintain a list of remaining handlers THIS is expensive. To maintain the list of remaining handlers is "more" than to unsubscribe some handlers that have already been unsubscribed. You cannot claim that you are doing "more than necessary" by unsubscribing some handlers that have already been subscribed while at the same time ignoring that you are doing "more than necessary" by maintaining a list of remaining handlers.
> do only what's necessary 'means: only detach remaining handlers > 'when closing the form (aka logical) Can you not see, in your own way of presenting the situation, that in the supposed "do more than necessary" clause, you have only one action, while in the supposed "not do more than necessary" clause, you have two? How is doing one thing "more" than doing two things?
> end if > else [quoted text clipped - 3 lines] > - You are still trying to write the lower Else part, but the first > If-expression has already been evaluated True. That's the precondition. No, I am not "still trying to write the lower Else part. I am trying to explain to you that it does not matter what the first "if" condition is. The "do the least necessary" answer is still to simply traverse the entire data structure when the form closes.
> The corresponding Else part is completely out of interest. Feel free to > discuss it with somebody else but it's not what I am interested in. Nor am I. In nearly every post, I explain to you that it's irrelevant, and yet in every reply you come back and tell me that you think I'm still talking about it. How many times do I have to tell you I'm not talking about that before you figure out that I'm not talking about that?
> - At the second If, you decide for True because you want to avoid the > "maintain a list..." job, whereas I decide for the else block. I do > accept > the additional programming work ("maintain...") in order to "do only > what's > necessary", whereas you don't. I'm not talking about additional programming work. I'm talking about code execution work. That said, you have MORE of BOTH. In what way is that not "doing more than is necessary"?
> Our different decisions at the 2nd "If" are caused by differently > weighting > the pros and cons. Only if by "differently weighting the pros and cons" you mean "Armin is completely ignoring the cost of maintaining the list of not-yet-removed handlers".
You claim the decision is about "not doing more than is necessary", and yet your solution is definitely to do more than is necessary.
Now, if you have some other criteria than how much you are "doing", that's a different matter. But you have to state that criteria for me to comprehend it. So far, all you are writing is "doing more than necessary", and the way I interpret that is that you are talking either about the work in programming, the work of the code executing, or both. From what you've written, I have narrowed that down to "the work of the code executing", but even by that criteria, your conclusion is wrong.
> For me, the True block is almost unacceptable whereas you > accept it. But you have not stated a logically correct reason for it to be that "the True block is almost unacceptable".
> To me it is almost unimportant how much work it takes to avoid an > almost unacceptable situation. The additional reason to choose the Else > block was pure technical interest in whether and how it is possible. > Meanwhile I know that it is not possible the way I was looking for, > nevertheless it is possible. Therefore I do it. If it wasn't possible at > all, I would have to swallow the bitter pill and choose the True block. Why is writing less code, and executing less code, a "bitter pill"? For most people, that's a win-win situation. For some reason, you are completely against it. You spend less time working on the code, it's simpler and so less likely to have bugs, and it takes less time to run. What's so "bitter" about that?
> So, we won't find an agreement because we have different weightings.
> No problem. I will repeat my arguments until you got them and see that > your's are illogical. You have less of an understanding of "logic" than you appear to think you have.
> I thought we are grown up enough to avoid this endless > loop. Obviously you are not. Anyways, I won't have to add anything new. You already did. I've replied to it (see above).
Pete
Armin Zingler - 12 Jul 2007 14:06 GMT As you are not even open to my attempts for an objective explanation of the situation - which I tried with my VB p-code - the alternatives, their costs and of why we disagree, I start thinking you are too narrow-minded for a further discussion. This shall not be my problem anymore.
I don't know why you are not able to accept that we think different, that we evaluate situations differently, that we call different situations (un)acceptable, that we consequently decide different, and that we have a different understanding of logic and clean programming, and we even use different words (that are well understood in my native language, sorry) for the same thing. As I've always been appreciated for my efforts to use clean approaches and for my logical and clean programming I keep on doing it my way. (I know you consider it all wrong and I know nothing about everything.)
Though, thanks for participating in this discussion. I'm off...
Armin
Peter Duniho - 12 Jul 2007 18:07 GMT > As you are not even open to my attempts for an objective explanation of > the > situation - which I tried with my VB p-code Not only am I open to those attempts, I used them to point out to you the fallacy in your argument. Claiming that I am "not even open" to your approach is a non-starter and demonstrably false, as I have at every step of the way been happy to discuss the issue in whatever context you've offered.
> - the alternatives, their costs > and of why we disagree, I start thinking you are too narrow-minded for a > further discussion. This shall not be my problem anymore. You have chosen a particular design and refuse to consider the possibility that a different one might be better, and _I_ am the one who is "too narrow-minded"? That's funny!
> I don't know why you are not able to accept that we think different, that > we evaluate situations differently, If by that you mean that I evaluate the situation based on concrete, objective criteria, while you are unable to state what your actual design goals are, then yes...we do evaluate situations differently.
> that we call different situations (un)acceptable, I haven't called any situation "unacceptable". What I have done is point out your failure to _explain_ why you consider a particular situation "unacceptable".
> that we consequently decide different, Yes, I am deciding using objective measures while you appear to be using intuition. A classic Meyes-Briggs personality conflict.
> and that we have > a different understanding of logic and clean programming, Two people cannot have a "different understanding of logic". One can understand logic while another does not, but logic is inherently rigorous and not open to subjective differences of understanding.
Since I have publicly stated the basis and analysis behind my logic, while you have not done so, I think it's pretty clear here that I understand logic. I leave the rest of the conclusion to the reader.
> and we even > use different words (that are well understood in my native language, > sorry) for the same thing. I use the words that match those used by Microsoft in their .NET documentation. You use terminology that you made up. However, whatever the case, differences in terminology have NOTHING to do with _this_ thread. I understand that you are still sore about being chastised for misusing the word "catch", but you need to get over it. And it has nothing to do with this thread.
Pete
John Saunders [MVP] - 12 Jul 2007 03:40 GMT > [...] >> > > > Why keep listening to events that will never occur? [quoted text clipped - 29 lines] > removement of the reference. Instead, I simply remove it because I don't > need it anymore. That's the basis rule. Technology changes. That changes basic rules. This rule, which used to be basic, needs to be challenged in an environment that includes garbage collection.
In .NET, it is only rarely a good idea to follow your basic rule.
 Signature John Saunders [MVP]
Armin Zingler - 12 Jul 2007 12:59 GMT > > Let me put it this way: > > A basic programming rule that I (and not only I) always obey is: [quoted text clipped - 10 lines] > > In .NET, it is only rarely a good idea to follow your basic rule. -- Well, in the German VB.Net group, there were 4 people that agree with me in this point (4 out of 4). Even the GC doesn't collect objects that are still referenced. It is common practice and considered clean programming to remove references to objects that are not needed anymore. Maybe you haven't heard of it yet.
Armin
John Saunders [MVP] - 12 Jul 2007 14:56 GMT >> > Let me put it this way: >> > A basic programming rule that I (and not only I) always obey is: [quoted text clipped - 18 lines] > remove references to objects that are not needed anymore. Maybe you > haven't heard of it yet. That's it, I'm sure.
 Signature John Saunders [MVP]
Damien - 12 Jul 2007 15:57 GMT > > > Let me put it this way: > > > A basic programming rule that I (and not only I) always obey is: [quoted text clipped - 18 lines] > > Armin Hi Armin,
Sorry to butt in, possibly with more unwelcome advice. Have you considered the fact that it used to be best practice to always free memory as soon as possible, but that now under the GC, the freeing of memory is done when it is required/when it is convenient.
Now, apply this analogy to the cleaning up of your event handlers. You're "cleaning up as soon as possible". Peter and John are advocating "cleaning up when it's convenient".
Let's also consider the fact that, in this case, you have to perform this clean up in order to ensure that the overall system continues to run efficiently (no "orphan" objects attached to live objects). In this case, you have to ensure that resources get cleaned up no matter what - in the face of possible exceptions and the like - which would surely lead to you using John/Peters suggestion anyway (possibly with a comment above it, if you think it's going to trip you/others up later such as "Of course, some of these may have already been removed").
Damien
PS - your survey of 4 people on the German newsgroup about which option would be advocated may or may not be significant - it may come down to how you've phrased the problem. Thus far, on this newsgroup, you've got 2 knowledgable individuals recommending a different approach. I've stayed silent thus far because I agreed wholeheartedly with what they were saying.
Armin Zingler - 12 Jul 2007 18:48 GMT > On Jul 12, 12:59 pm, "Armin Zingler" <az.nos...@freenet.de> wrote: > > [quoted text clipped - 25 lines] > > Sorry to butt in, possibly with more unwelcome advice. Nah, no problem. :-) Advice is always welcome.
> Have you > considered the fact that it used to be best practice to always free > memory as soon as possible, but that now under the GC, the freeing > of memory is done when it is required/when it is convenient. I know this, but if you don't release references, the GC does not even has the /chance/ to free memory. That's why it is common practice to release references as soon as possible. Not in order to free memory immediatelly but to make it possible. (But I don't go as far as some people that even set /local/ variables to Nothing at the end of a procedure. That's too much for me).
> [...] I'm afraid, there is nothing new I can add. Instead, I can offer a (IMO) good comparison:
Imagine, you know 50 people. From time to time you lend things to them. Every 12/31 you want all lended things back. You have two choices:
a) At 12/31, call all the people and tell everyone to give the things back. This means, calling also those people that have already given everything back. It doesn't hurt to call them, but I call it "illogical" (please replace it by the word of your choice). The phone costs or the time that it takes is not an issue, but simply doing it although it is not necessary is IMO not acceptable.
b) Everyone who borrows anything is put on a list. As soon as it's given back, he is removed from the list. At 12/31, only the people on the list are called.
You won't be suprised that I favor b) even if I have to maintain a list. I do this because I consider it the "cleaner" approach.
I guess you favor a) ? :-) I accept this opinion - I just don't want to be handled like an idiot because mine is different.
> PS - your survey of 4 people on the German newsgroup about which > option would be advocated may or may not be significant - it may > come down to how you've phrased the problem. The question in the other group was only about "release references as soon as possible". I referred to John's mentioning of the "basic rule" (which is the immediate release). I only asked there to insure myself that I was not seeing things throughout the past years. :-)
> Thus far, on this > newsgroup, you've got 2 knowledgable individuals recommending a > different > approach. I've stayed silent thus far because I agreed > wholeheartedly with what they were saying. Though, thanks for your opinion. I like the way you express it. Thank you! (and this is not ironic)
Armin
Peter Duniho - 12 Jul 2007 19:11 GMT > [...] > Imagine, you know 50 people. From time to time you lend things to them. [quoted text clipped - 17 lines] > I > do this because I consider it the "cleaner" approach. Your analogy fails because a phone call is far more expensive than writing something on a list. Also, you are dealing with numbers that are so small that managing the list is relatively easy, even for a human being.
Those characteristics do not translate to your problem. In particular, the unsubscribing operation is incredibly _inexpensive_. Much less expensive than the act of maintaining _any_ list, however small. And while maintaining your list of subscribed handlers isn't too bad as long as the number is small, code should be scalable and your approach will incur greater and greater performance penalties as the number of nodes in your tree grows.
> I guess you favor a) ? :-) I accept this opinion - I just don't want to > be > handled like an idiot because mine is different. Nobody is treating you like an idiot. Don't confuse the act of point out your mistakes as treating you like an idiot.
> The question in the other group was only about "release references as > soon > as possible". Then that is entirely inapplicable to the point I have been making. As I have stated each time you bring it up: you may unsubscribe the event handlers as soon as you like. I wouldn't bother with it myself, but it's not a fundamental design problem.
So, the answer you receive in the other group is entirely irrelevant to the point I am making.
Pete
Armin Zingler - 13 Jul 2007 01:22 GMT A friend told me: "I am adding zero to zero all day long". I asked: "Why do you do this?" He answered: "Because it goes sooo quick and it doesn't hurt!" Me: "Ummmm.... yes, that makes a LOT of sense......." Me: "But why don't you visit a psychiatrist to stop this?" He said: "It costs!" Me: "Ah, I see...But wouldn't it be nice to stop this even if it costs?" He said: "But why? It goes so quick and it doesn't hurt!"
I stopped trying to convince him.
Peter Duniho - 13 Jul 2007 03:37 GMT > A friend told me: "I am adding zero to zero all day long". > [...] > > I stopped trying to convince him. So you're trying to say I should stop trying to convince you?
Damien - 13 Jul 2007 08:10 GMT > > Hi Armin, > [quoted text clipped - 13 lines] > /local/ variables to Nothing at the end of a procedure. That's too much for > me). Okay. I was just trying to highlight the fact that what was once a best practice of "release immediately" (with respect to freeing memory) is now a) not followed, and b) because of the GC design, not even possible - it's all entirely under the GCs control, and it follows a different strategy.
> > [...] > [quoted text clipped - 20 lines] > I guess you favor a) ? :-) I accept this opinion - I just don't want to be > handled like an idiot because mine is different. I guess what Peter especially is trying to get you to focus on is the fact that all strategies for release have attendent overheads. Have you ever done any robust COM programming in a C derived language? It's amazing how complex it is to just make sure your reference counting code is correct, and handles the edge cases - and that was because the COM model was based around the "release as soon as possible" approach.
I know you've stated you're not looking at this from a performance perspective, but from a "clean code" perspective. In Peter's code, we can see 1 operation, in 1 place, possibly with the comment "Some of these may have already been removed", which goes through the whole tree and removes all of the handlers. I can go and look at that one chunk of code, and know that I am *sure* that all of the handlers are being removed when the form closes.
Contrast this with your solution. I have to read this "same" chunk of code, and see that it is removing only those handlers which haven't been removed. But I then need to go and review each handler, and verify that it is removing itself correctly. And I then need to examine all of the list maintenance procedures, and ask myself questions such as "is this list operation thread safe?", "Does it need to be?". Only after I've done all of that would I be as confident that your solution is removing all of the handlers.
And even if I had verified your solution, I'd probably still go back to the final "chunk" which does the cleanup and remove the list checks, and let it remove all of the handlers (again, adding the comment if you so wish), because a) I know that the removal code is cheeper to run than the list checkup code (okay, that is a performance argument), BUT b) because I'm going to use a belt and braces approach.
Sorry - I don't know if you know or understand the phrase "belt and braces". I don't know if there's a German equivalent. It refers to the practice of ensuring something happens (in this case, your trousers staying up) by the use of more than method (using a belt, and also using braces).
This post turned a bit epic. Apologies.
Damien
Armin Zingler - 13 Jul 2007 12:29 GMT > On Jul 12, 6:48 pm, "Armin Zingler" <az.nos...@freenet.de> wrote: > > > Hi Armin, [quoted text clipped - 21 lines] > even possible - it's all entirely under the GCs control, and it > follows a different strategy. I know this all. I already wrote that it is not done to release memory immediatelly, but to give the GC the /chance/ to clean up objects. Without releasing unrequired references, objects will /never/ be destroyed.
> > > [...] > > [quoted text clipped - 30 lines] > that was because the COM model was based around the "release as soon > as possible" approach. I've heard of it. :-) Fortunatelly, in VB classic it was simpler to handle, though I know what was going on under the hood (addref/releaseref...). I like the GC approach more.
> I know you've stated you're not looking at this from a performance > perspective, but from a "clean code" perspective. In Peter's code, > we can see 1 operation, in 1 place, possibly with the comment "Some > of these may have already been removed", Right. The very last sentence is the reason why it is not an acceptable solution (for me). I'm afraid, I can not add anything new to the rest of your posting.
Doing it Peter's way is /intentionally/ writing code that /intentionally/ is sometimes doing things that do not have to be done. Even if those superfluous things are "inexepensive" and "don't hurt" (just like adding zero to zero which also doesn't make sense...), why should I decide for intentionally writing this code? That's what I call an "illogical" decision (you may call it different, of course). I don't want to write illogical code. I accept writing more code in order to prevent me from writing illogical code - yes, even if it costs. Sometimes the logical way is longer. Peter's solution has such a big drawback that it is not acceptable for me. You probably don't consider it being a "big" drawback, but for me it so big that it is /unacceptable/.
/As a consequence/, I /had to/ find a way to avoid the situation above. Assuming this, the next decision was to "maintain a list...". That's an obvious decision (because if you want to store an unknown number of items, a list/arraylist/whatever is what a programmer does).
What has always been ignored in this thread is the 2nd reason why I asked here: As an experienced programmer - I guess all the people in this thread are - I came to the point where I wondered how to store a certain piece of information. Programmers store and retrieve information all day long, so we know how it works. But in this case I did not know how to do it: How can I store information in variables that can later be used with the Addhandler/Removehandler keyword? (in addition: without handling each object type and event individually to be able to maintain it in a single list; meanwhile I know that it is not possible and I accepted it)
-> This is just a technical question <-
Don't you sometimes want to learn things just for the sake of learning? It doesn't matter whether you will ever use the knowledge, but you are just interested in it. In this case, I felt that the question was very interesting because i haven't had the case not to know how to store a piece of information for a long time. Therefore I asked here.
Armin
Damien - 13 Jul 2007 13:55 GMT > > On Jul 12, 6:48 pm, "Armin Zingler" <az.nos...@freenet.de> wrote: > > > > Hi Armin, [quoted text clipped - 85 lines] > You probably don't consider it being a "big" drawback, but for me it so big > that it is /unacceptable/. I'll try to put my point more briefly than last time. Because having 1 small piece of code where I can see exactly what is happening is easier to comprehend, and be sure it is correct, than a large number of blocks of code, through the source, which all have to be read and understood before you can be sure that the code is doing the right thing.
You example of adding zero being a wasted operation? How would you write the following function:
Function ABC takes two arrays (which most both be 1 dimensional arrays of integers with the same length). It must return a new array, of the same length as the inputs, where the value at each position is the sum of the input values at the same position. (Exception throwing code for length mis-match omitted).
Option 1:
Public Function ABC(ByVal ar1() As Integer, ByVal ar2() As Integer) As Integer() Dim out(ar1.GetUpperBound(0)) As Integer
For i As Int32 = 0 To ar1.GetUpperBound(0) out(i) = ar1(i) + ar2(i) Next Return out End Function
Option 2:
Public Function ABC(ByVal ar1() As Integer, ByVal ar2() As Integer) As Integer() Dim out(ar1.GetUpperBound(0)) As Integer
For i As Int32 = 0 To ar1.GetUpperBound(0) If ar1(i) = 0 Then If ar2(i) = 0 Then out(i) = 0 Else out(i) = ar2(i) End If Else If ar2(i) = 0 Then out(i) = ar1(i) Else out(i) = ar1(i) + ar2(i) End If End If Next Return out End Function
I hope you'll stick to your guns and choose Option 2, since Option 1 sometimes performs addition operations it doesn't have to.
[snip to end]
> -> This is just a technical question <- > [quoted text clipped - 5 lines] > > Armin Absolutely. I love learning new things. I frequently don't know the answer to questions people pose on these newsgroups. But if the question is interesting, I'll sometimes spend some time looking into the question. Even if I can't then answer the question, I'll usually have learnt something new. Or I'll provide half an answer and invite others to extend it to where I cannot - and learn from that.
The day I stop learning is the day I die.
Damien
Armin Zingler - 13 Jul 2007 17:38 GMT > > Right. The very last sentence is the reason why it is not an > > acceptable solution (for me). I'm afraid, I can not add anything [quoted text clipped - 17 lines] > 1 small piece of code where I can see exactly what is happening is > easier to comprehend, I would have my problems to explain the, at first sight, more comprehensive code to somebody else. If my boss (although I don't have one...) looked at it, he would probably ask me: "You are removing handlers immediatelly, right?". Me: "Right". He: "At the end, you are removing also those handlers that already have been removed. Maybe all handlers have already been removed, though you are really removing all handlers twice? It doesn't hurt but it also doesn't make sense, so change this!". I'll change the code because I see that he is right - even if it is extra work.
I think, code must be explainable. Code that sometimes does superfluous things is not explainable.
> and be sure it is correct, than a large number > of blocks of code, through the source, which all have to be read and > understood before you can be sure that the code is doing the right > thing. I don't have a problem in understanding the code. It is not an argument to not write code because it will have to be understood by anybody in the future because this is true for /any/ code. If the code has to be written, it has to be done. (I know that you think it doesn't have to be written).
> You example of adding zero being a wasted operation? How would you > write the following function: [quoted text clipped - 3 lines] > I hope you'll stick to your guns and choose Option 2, since Option 1 > sometimes performs addition operations it doesn't have to. Good example that makes me think about it. I would choose Option 1. The analogy fails because your Option 2 translated to my case would have to be (pseudo code):
for each item in list if item.handlerisattached then item.removehandler end if next item
That's what I do /not/. Would be even too much for me (just as too much as "if a isnot nothing then a = nothing").
I apologize that I won't discuss this example additionally, because I really want to come to an end, primarily because my solution runs since...
> > -> This is just a technical question <- > > [...] [quoted text clipped - 6 lines] > > The day I stop learning is the day I die. I completely agree.
Armin
Rory Becker - 12 Jul 2007 18:54 GMT > PS - your survey of 4 people on the German newsgroup about which > option would be advocated may or may not be significant - it may come > down to how you've phrased the problem. Thus far, on this newsgroup, > you've got 2 knowledgable individuals recommending a different > approach.
> I've stayed silent thus far because I agreed wholeheartedly > with what they were saying. +1
Appologies for not adding much to this except my vote, but Damien has said it all quite nicely.
-- Rory
Peter Duniho - 12 Jul 2007 19:04 GMT > Sorry to butt in, possibly with more unwelcome advice. Have you > considered the fact that it used to be best practice to always free > memory as soon as possible, but that now under the GC, the freeing of > memory is done when it is required/when it is convenient. [...] As you can see (from his reply), the question of whether or not to unsubscribe the event handlers as soon as they can be continues to distract Armin from the real issue: how to clean up when the form is closed.
IMHO, it would be better to leave off the question about whether to unsubscribe the handlers as they are used. I agree (obviously) that there's no need to unsubscribe the handlers except at the end. But since it's still more efficient to simply traverse the tree an unsubscribe all of the events, even those that have already been unsubscribed, the question of whether or not he unsubscribes them as they are used is just a red herring.
The real question here IMHO is whether to make any attempt to track which event handlers have been unsubscribed. I take as granted (because Armin insists) that the event handlers _will_ be unsubscribed as they are used, and I still come to the conclusion that a simple tree traversal at the end to unsubscribe all of the events is superior to any sort of scheme attempting to track what's left to unsubscribe.
I also note that even if one is going to track what's left to unsubscribe, Armin's solution is inefficient and far too complex. But that's yet another issue, and since I don't agree that the tracking should be done in the first place, I won't waste much time talking about that. :)
Pete
John Saunders [MVP] - 11 Jul 2007 00:08 GMT > Hi, > [quoted text clipped - 11 lines] > - 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.
 Signature John Saunders [MVP]
Armin Zingler - 11 Jul 2007 00:57 GMT > "Armin Zingler" <az.nospam@freenet.de> wrote in message > news:eEhlPQzwHHA.3364@TK2MSFTNGP02.phx.gbl... [quoted text clipped - 19 lines] > 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
Peter Duniho - 11 Jul 2007 01:53 GMT > 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 [quoted text clipped - 6 lines] > 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 [quoted text clipped - 3 lines] > 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
Peter Duniho - 11 Jul 2007 02:43 GMT > [...] >> It shows that I currently have to handle each event individually. It [quoted text clipped - 3 lines] > 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
Armin Zingler - 11 Jul 2007 04:43 GMT > 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.
> > Though, it is still interesting for me that I can not store the > > information [quoted text clipped - 5 lines] > > 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]
> [C] Sorry, I can not comment the C-Code.
> [C#] > [...] [quoted text clipped - 6 lines] > 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: [quoted text clipped - 16 lines] > 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.
> > 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, 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
Peter Duniho - 11 Jul 2007 05:27 GMT > 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 [quoted text clipped - 11 lines] > [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.
>> 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 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 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. 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"?
>> > It shows that I currently have to handle each event individually. >> > It was a lot to type even though there were only two events. [quoted text clipped - 11 lines] > > 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 [quoted text clipped - 3 lines] > 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 [quoted text clipped - 3 lines] > 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 [quoted text clipped - 3 lines] > 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 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
Armin Zingler - 11 Jul 2007 12:23 GMT > [...] 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
|
|