Object lifetime/event problem

B

Barry Gilbert

I have a class that raises events that downstream objects subscribe to. In
one case, after destroying the object, the event seems to still get handled
in a subscriber object. So I instantiate an object and the event fires
correctly. Then I destroy the object and instantiate another instance and the
subscriber event handler get hit twice. I've tried using IDisposible and also
using GC.Collect, but it didn't seem to help. I've also tried using
RemoveHandler before destroying the object.

It's not clear to me whether the handler is firing because the objects are
still in memory or for some other reason.

Any help would be appreciated.
 
J

Jeroen Mostert

Barry said:
I have a class that raises events that downstream objects subscribe to. In
one case, after destroying the object, the event seems to still get handled
in a subscriber object. So I instantiate an object and the event fires
correctly. Then I destroy the object and instantiate another instance and the
subscriber event handler get hit twice. I've tried using IDisposible and also
using GC.Collect, but it didn't seem to help. I've also tried using
RemoveHandler before destroying the object.

It's not clear to me whether the handler is firing because the objects are
still in memory or for some other reason.

Any help would be appreciated.

First of all, you can't explicitly "destroy" objects. You can call
..Dispose() on them, but that's it. That does not in itself do anything; the
object itself has to release resources. An object will stick around for as
long as there are references to it; when the last reference disappears, the
garbage collector will *eventually* reclaim the memory. Threads do not
belong to objects and will stick around until they either exit themselves or
(if flagged as background threads) the process exits.

When does your class raise events? In response to what? My guess is that
whatever you're using for that isn't stopped or signaled properly. The only
way to prevent the events from being raised is simply to not raise them. You
should know when not to raise them anymore because you should know when your
object is done doing whatever it's supposed to do (because someone called
..Dispose() or .Stop() or whatever, or because an external condition occurs).
 
B

Barry Gilbert

Jeroen,

Thanks for your reply.
My class raises an event after a transaction has been completed from within
it. The event is a notification to the UI that the transaction was successful
and contains, in its args, contextual information.

Let me try to give a better illustration of my problem.

I have a custom event in Class A. It is raised whenever a specific method
call to it has been successful.
Class B is a facade class that instantiates ClassA objects, listens for
ClassA events, and re-raises a new event that is consumed by the UI.
The order of events looks like this:
1-The UI creates a new instance of ClassB.
2-ClassB creates an instance of ClassA.
3-ClassA receives a method call that says "Save data" and, if successful,
raises a DataSaved event.
4- ClassB handles the ClassA event and raises a wrapper event that passes
the event args up to the UI.
5- In ClassB, the reference to ClassA is set to nothing.
6- In the UI, the reference to ClassB is set to Nothing.

The second time through, the event handler in ClassB receives two events
from ClassA. The code in ClassA that raises the event is not hit twice,
however. This is what leads me to believe that the previous instance of
ClassA is still in memory. However, I would assume that, even though the
object still lives in memory, all references to it, including delegates,
should not be accessible.

Does my description and confusion make sense?
 
J

Jeroen Mostert

Barry said:
My class raises an event after a transaction has been completed from within
it. The event is a notification to the UI that the transaction was successful
and contains, in its args, contextual information.

Let me try to give a better illustration of my problem.

I have a custom event in Class A. It is raised whenever a specific method
call to it has been successful.
Class B is a facade class that instantiates ClassA objects, listens for
ClassA events, and re-raises a new event that is consumed by the UI.
The order of events looks like this:
1-The UI creates a new instance of ClassB.
2-ClassB creates an instance of ClassA.
3-ClassA receives a method call that says "Save data" and, if successful,
raises a DataSaved event.
4- ClassB handles the ClassA event and raises a wrapper event that passes
the event args up to the UI.
5- In ClassB, the reference to ClassA is set to nothing.
6- In the UI, the reference to ClassB is set to Nothing.
This is bad form. Setting references to Nothing (null) generally
accomplishes nothing -- it does make objects eligible for garbage collection
if there are no other references, but unless the referenced object is the
parent of a huge collection of other objects you no longer need, this is
unlikely to be of concern.

Keep in mind that the garbage collector isn't guaranteed to run in any
amount of time, and setting a reference to null does not force deallocation
of any object. The only thing that nulling out a reference achieves is that
code can no longer *accidentally* use the object. It doesn't make any
*functional* difference.

If you're replacing the reference with a new instance, you can just do that,
and you don't have to bother with setting it to null first.
The second time through, the event handler in ClassB receives two events
from ClassA. The code in ClassA that raises the event is not hit twice,
however.

Here's where your description gets into trouble. Do you mean "the event
handler in a particular instance of class B receives two events from a
particular instance of class A"? Have you checked whether "this" (Me) is the
same in both cases?

If B attached an event handler to the same event twice, this would be the
behavior to expect: the event handling code only runs once, but it calls
both attached delegates.
This is what leads me to believe that the previous instance of
ClassA is still in memory. However, I would assume that, even though the
object still lives in memory, all references to it, including delegates,
should not be accessible.
You're formulating it backwards. An object still lives in memory if there
are still references to it. If there are no references to it, it's no longer
"alive" (its bits might still be around, but you have no way to tell, by
definition). So either there are no references and there is no object, or
there are still references and there is an object. It cannot be that there
is an object when there are no references -- that just means you've got the
wrong idea about the references.

Your code doubtlessly has a bug, but exactly what it is I still can't say. :)
 
J

Jack Jackson

I have a class that raises events that downstream objects subscribe to. In
one case, after destroying the object, the event seems to still get handled
in a subscriber object. So I instantiate an object and the event fires
correctly. Then I destroy the object and instantiate another instance and the
subscriber event handler get hit twice. I've tried using IDisposible and also
using GC.Collect, but it didn't seem to help. I've also tried using
RemoveHandler before destroying the object.

It's not clear to me whether the handler is firing because the objects are
still in memory or for some other reason.

Any help would be appreciated.

Setting a reference to null does not make an object go away. It just
makes it eligible for garbage collection.

Suppose for example I create an instance of class X. In the
constructor the class creates an instance of a timer and sets the
timer to fire every second.

Immediately after creating the instance I set the reference to it to
null. Until the garbage collector runs and collects this instance,
the timer will continue to fire. The instance is still there, and
until the garbage collector runs and checks things no one knows that
there are no longer any references to the instance.

You say "the subscriber event handler get hit twice". Is that twice
for the same instance, or once for two different instances?

If twice for the same instance, then you have subscribed to the event
twice.

If for two different instances, then you need to find out why the
class that you no longer have references to is still generating
events.

Perhaps you need to implement IDispose in your classes, and have the
Dispose method 'turn off' the class - make it stop doing whatever
generates events. Then if you dispose of the class when you are
finished with it, it will stop generating events.
 
A

Adam Benson

When you destroy your subscriber object do you remove the event handler?

i.e.

// Shut down my subscriber object
Close()
{
WhereTheEventsComeFrom.MyEvent -= new Event( this.EventHandlerRoutine );
}

If you don't do that the object raising the event still has a reference to
the subscriber. The result of this is that the subscriber will remain in
memory. Creating another one adds a new instance of the event handler - 2
objects now exist in memory, each one subscribing to the event.

I know you say you call RemoveHandler but the fact that your event is firing
twice suggests to me that your original object is still around, and is still
subscribing to the event.

HTH,

Adam.
 

Ask a Question

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

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

Ask a Question

Top