Question

I'm working on an OSS project to make the popular MediaInfo library easier to use in .NET, but this question is generalizable.

If a derived class D always instantiates an object O when calling its base class DB's constructor. DB sets its value to that sent to its constructor, but the value itself is declared in DB's base class B:

  1. Who "owns" O (AKA mediaInfo in the code below)?
  2. In the case of a .NET application, which of these should implement IDisposable? Note: O is unmanaged, or at least is an instantiation of a managed object wrapped around an unmanaged library, but does need cleanup in the form of "MediaInfo.Close();". I am not certain this counts as "unmanaged."

To help clarify, let me use actual code:

D derives from DB:

// MediaFile is "D" 
public sealed class MediaFile : GeneralStream
{
    public MediaFile(string filePath)
        : base(new MediaInfo(), 0) {
        // mediaInfo is "O"
        mediaInfo.Open(filePath);
    }
}

DB sets its inherited O, derived from B:

// GeneralStream is "DB"
public abstract class GeneralStream : StreamBaseClass
{
    public GeneralStream(MediaInfo mediaInfo, int id) {
        this.mediaInfo = mediaInfo; // declared in StreamBaseClass
        // ...
    }
}

B declares O:

// StreamBaseClass is "B"
public abstract class StreamBaseClass
{
    protected MediaInfo mediaInfo; // "O" is declared
    // ...
}
Was it helpful?

Solution

The object, which holds a reference to the resource, owns it.

StreamBaseClass has the reference mediaInfo and should implement IDisposable. The reference and the Dispose method will automatically be inherited by the deriving classes.

OTHER TIPS

If a class C owns a variable which is a non-exposed local variable V which implements IDisposable, then C should be IDisposable and C's IDisposable should dispose V.

If a class D owns a native resource N, then D should be IDisposable (which deletes N) and should also have a finalizable destructor which calls Dispose() on itself to release N.

If you follow this pattern, then if you ever have an IDisposable, you should always Dispose() it when you're done and that will delete everything all the way down the object tree; but also if someone forgets to you (read: a colleague, user of your library etc) won't leak any objects because the native resource will be cleaned up by D's finalizer.

The responsibility of an IDisposable belongs to the object that creates it, in the absence of any explicit agreement otherwise. Contrary agreements are generally used in cases where the creator of a resource may have no idea of the consumer's lifetime. I would suggest that in many cases when a constructor or factory method is producing what may be the last consumer of a passed-in IDisposable, that method should accept a parameter indicating whether it should accept responsibility for calling Dispose, or else accept a callback delegate which will, if non-null, be invoked when the consumer no longer needs the object. If the creator of the object will outlive the consumer, it can pass null; if the creator will have no use for the object once it's been handed off, it can pass the object's Dispose method. If the creator doesn't know whether it will outlive the consumer, it can pass a method which will determine whether the object is still needed and call Dispose if not.

With regard to your particular case, constructing an IDisposable within a chained constructor call is a recipe for resource leaks (since there's no way to wrap chained constructor calls in a try-finally block). If you were to somehow handle that safely (e.g. using a factory method rather than a chained constructor, or by using a [threadstatic] hack), I would suggest that since the creator of the object (the derived class) is going to know the lifetime of the consumer (the base class), ownership and cleanup responsibility should stay with the object creator.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top