ddl877
ddl877

Reputation: 43

C#: How to Dispose a contained object as a result of it's own event?

Background:

I've a got a main object that live on my app's UI thread. This long-lived parent / container object contains a sub-object as a private member that is internally multi-threaded.

Based on some event published from the child / contained object I'd like the parent object to simply Dispose() the child and recreate it.

The contained object owns some unmanaged resources and well as some large managed memory buffers.

I could create an event handler on the top level object that does this, but this means that the object about to be Disposed will be in the call stack of the method call to about to replace it. (!)

This is because the event handler delegate will be called by one of the child object's state handling functions on it's own thread.

This seems...wrong. No?

In particular, the the child's FireAnEvent() method will resume execution after the delegate calls are processed, except that now execution will resume in the context of an already "disposed" object.

Intuitively, I can't see this leading to good things.

Question:

Is there an established C# pattern to destroy a contained object as a result of it's own event?

Or, is there GC magic that makes such a simple event handler good enough somehow?

Or, am I missing some key bit of understanding?

Upvotes: 0

Views: 566

Answers (3)

supercat
supercat

Reputation: 81277

Objects which subscribe to events for the purpose of notification should be prepared to receive notifications at any time, even after they have been disposed. The purpose of a notification is to tell an object to do whatever it needs to do in response to something that has happened. If an object can't do anything useful in response to a notification, it should simply not do anything.

Further, the purpose of Dispose isn't to "destroy" an object, nor the resources it contains, but rather to release it from any obligations it may have had to outside objects, and allow it to release any outside entities from any obligations they might have had toward it. In many cases, an object will be useless once it releasing the services of outside entities that were committed to it, and thus objects which have been disposed cannot be expected to be useful; if, as is likely, a method which is called after Disposed cannot satisfy its duties because necessary outside entities have been released, it should throw ObjectDisposedException rather than failing some other way.

Putting these observations together, while many methods on a disposed object should throw ObjectDisposedException, notification event handler methods should not, since they're instructing the object to "do whatever you need to do to meet your obligations, given that something has happened". If an object has been disposed, it has no obligations. Thus, being disposed doesn't prevent an object from satisfying an event handler contract; instead, it allows the object to meet the contract by silently ("successfully") doing nothing.

Upvotes: 0

Assaf
Assaf

Reputation: 1370

as mentioned, an IDisposable object is nothing magical. It just lets you use the using shorthand, which is just a shorthand for:

try { // code in the using block }
catch{}
finally{
    disposableObject.Dispose()
}

Have you considered incorporating a third type of object into the mix? It is ill advised that contained object be conscious of their container. Roughly this would be your workflow:

  1. contained object decides it should be restarted.
  2. contained object frees resources.
  3. contained object writes to a queue on a third object (not the containing object).
  4. containing object accesses the queue when you feel you should create new contained objects and reinstantiates the objects. Alternatively adding to the queue raises an event to the container to empty it.

The third object might seem pointless but it would make your life a lot easier if you ever decided to refactor.

Upvotes: 0

Taudris
Taudris

Reputation: 1443

Calling IDisposable.Dispose() doesn't signal anything special to the .NET framework. The only thing you need to do is remove any references to the object you wish to remove. Once this is done and the object is out of the call stack, it will become a candidate for garbage collection.

Note that your object will not necessarily be garbage collected immediately, or even the next time the GC runs; it is merely an assumed eventuality.

The only purpose of IDisposable is to provide a standard means for requesting that an object clean itself up and release resources. You can hold a reference to a "disposed" object for as long as you like, which will prevent the GC from collecting the object. IDisposable.Dispose() is just another method; technically, you can make it do anything you want.

This question has a very nicely detailed answer that may help you understand IDisposable a bit more: Proper use of the IDisposable interface

Upvotes: 1

Related Questions