哪个类“拥有”非托管资源(并实现IDisposable)?

时间:2012-03-11 18:25:34

标签: c# .net unmanaged idisposable mediainfo

我正在研究an OSS project以使流行的MediaInfo library更容易在.NET中使用,但这个问题可以推广。

如果派生类 D 在调用其基类 DB 的构造函数时始终实例化对象 O 。 DB将其值设置为发送到其构造函数的值,但值本身在 DB 的基类 B 中声明:

  1. 谁“拥有” O (以下代码中的AKA mediaInfo)?
  2. 对于.NET应用程序,哪些应该实现IDisposable? 注意: O 是非托管的,或者至少是围绕非托管库的托管对象的实例化,但需要以“ MediaInfo.Close(); ”。我不确定这算是“不受管理的”。
  3. 为了帮助澄清,让我使用实际代码:

    D 来自 DB

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

    数据库设置其继承的 O ,源自 B

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

    B 声明 O

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

3 个答案:

答案 0 :(得分:2)

该对象拥有对资源的引用,拥有它。

StreamBaseClass的引用为mediaInfo,应该实现IDisposable。引用和Dispose方法将自动由派生类继承。

答案 1 :(得分:1)

如果C类拥有一个变量,该变量是非暴露的局部变量V,它实现了IDisposable,那么C应该是IDisposable而C的IDisposable应该配置V。

如果D类拥有本机资源N,则D应该是IDisposable(删除N),也应该具有可终结的析构函数,该析构函数调用Dispose()以释放N.

如果你遵循这种模式,那么如果你有一个IDisposable,那么你应该在完成后始终Dispose()它将在对象树中一直删除所有内容;但是如果有人忘了你(读:同事,你的图书馆的用户等)不会泄漏任何对象,因为本地资源将被D的终结者清理。

答案 2 :(得分:1)

在没有任何明确协议的情况下,IDisposable的责任属于创建的对象。相反的协议通常用于资源的创建者可能不知道消费者的生命周期的情况。我建议在很多情况下,当构造函数或工厂方法生成可能是传入的IDisposable的最后一个使用者时,该方法应该接受一个参数,指示它是否应该承担调用{{1}的责任。或者接受一个回调委托,如果非空,则在消费者不再需要该对象时调用该委托。如果对象的创建者将比消费者寿命长,则它可以传递null;如果创建者在被移交后对该对象没有用处,它可以通过对象的Dispose方法。如果创建者不知道它是否会比消费者寿命长,它可以通过一种方法来确定是否仍然需要该对象,如果没有,则调用Dispose

关于您的特定情况,在链式构造函数调用中构造Dispose是资源泄漏的一个方法(因为无法在try-finally块中包装链式构造函数调用)。如果你以某种方式安全地处理它(例如使用工厂方法而不是链式构造函数,或使用IDisposable hack),我会建议,因为对象的创建者(派生类)将会知道消费者的生命周期(基类),所有权和清理责任应该留在对象创建者身上。