【问题标题】:IDisposable base class which owns managed disposable resource, what to do in subclasses?拥有托管一次性资源的 IDisposable 基类,在子类中做什么?
【发布时间】:2010-11-04 04:06:46
【问题描述】:

我有一个拥有托管一次性资源 (.NET PerformanceCounter) 的基类。我了解在类上实现 IDisposable 以便我可以在资源上显式调用 Dispose。从我看到的示例中,人们通常使用私有布尔成员变量“disposed”并将其在 Dispose 内部设置为 true。稍后,如果尝试访问公共方法或属性,如果“disposed”为真,则会引发 ObjectDisposedException。

在子类中呢?子类如何在其公共方法和属性中知道它们已被释放?起初我认为子类不需要任何特殊的东西(比如实现他们自己的 Dispose 版本),因为需要处理的东西只在基类中(假设子类不会添加任何数据需要显式处理)并且基类的 Dispose 应该处理它。子类是否应该仅仅为了设置自己的“已处理”成员变量而重写基类的虚拟 Dispose 方法?

这是所讨论的类层次结构的一个非常精简的版本。

class BaseCounter : IBaseCounter, IDisposable
{
  protected System.Diagnostics.PerformanceCounter pc;
  private bool disposed;
  public BaseCounter(string name)
  {
    disposed = false;
    pc = CreatePerformanceCounter(name);
  }

  #region IBaseCounter
  public string Name
  {
    get 
    {
      if (disposed) throw new ObjectDisposedException("object has been disposed");
      return pc.CounterName;
    }
  }
  public string InstanceName
  {
    get
    {
      if (disposed) throw new ObjectDisposedException("object has been disposed");
      return pc.InstanceName;
    }
  }
  #endregion IBaseCounter

  #region IDisposable
  protected virtual void Dispose(bool disposing)
  {
    if (!disposed)
    {
      if (disposing)
      {
        if (pc != null)
        {
          pc.Dispose();
        }
        pc = null;
        disposed = true;
      }
    }
  }

  public void Dispose()
  {
    Dispose(true);
  }
  #endregion IDisposable
}

class ReadableCounter : BaseCounter, IReadableCounter //my own interface
{
  public ReadableCounter(string name)
    : base(name)
  {
  }

  #region IReadableCounter 
  public Int64 CounterValue()
  {
    return pc.RawValue;
  }
  #endregion IReadableCounter
}

class WritableCounter : BaseCounter, IWritableCounter
{
  public WritableCounter(string name)
    : base(name)
  {
  }

  #region IWritableCounter 
  public Increment()
  {
    pc.Increment();
  }
  #endregion IWritableCounter
}

在我们的系统中,ReadableCounter 和 WritableCounter 是 BaseCounter 的唯一子类,它们只是通过代码生成过程再被子类化到一个级别。附加的子类级别只添加了一个特定的名称,以便可以创建直接对应于命名计数器的对象(例如,如果有一个计数器用于计算生成的小部件的数量,它最终被封装在一个 WidgetCounter 类中. WidgetCounter 包含允许创建“WidgetCounter”性能计数器的知识(实际上,只是作为字符串的计数器名称)。

只有代码生成的类被开发人员直接使用,所以我们会有这样的东西:

class WritableWidgetCounter : WritableCounter
{
  public WritableWidgetCounter
    : base ("WidgetCounter")
  {
  }
}

class ReadableWidgetCounter : ReadableCounter
{
   public ReadableWidgetCounter
     : base ("WidgetCounter")
   {
   }
}

因此,您会看到基类拥有并管理 PerformanceCounter 对象(它是一次性的),而子类使用 PerformanceCounter。

如果我有这样的代码:

IWritableCounter wc = new WritableWidgetCounter();
wc.Increment();
wc.Dispose();
wc.Increment();
wc = null;

WritableCounter 如何以 Increment 方式知道它已被处置? ReadableCoutner 和 WritableCounter 是否应该简单地覆盖 BaseCounter 的

protected virtual void Dispose(bool disposing)

类似这样的:

protected virtual void Dispose(bool disposing)
{
  disposed = true; //Nothing to dispose, simply remember being disposed
  base.Dispose(disposing); //delegate to base
}

仅仅设置一个ReadableCounter/WritableCounter级别的“disposed”成员变量?

如果基类 (BaseCounter) 声明为受保护的处置(或使其成为受保护的属性)呢?这样,子类可以引用它,而不是仅仅为了记住 Dispose 已经发生而添加 Dispose 方法。

我错过了这艘船吗?

【问题讨论】:

    标签: idisposable performancecounter


    【解决方案1】:

    我见过一些具有公共 IsDisposed 属性的一次性类。你可以这样做并在你的子类中检查它。

    我做的另一件事是一个通用的受保护的“验证”方法,所有子类方法都调用(并且可以覆盖)。如果它返回,一切都很好,否则它可能会抛出。这将使您的子类与一次性内脏完全隔离。

    【讨论】:

    • 我认为这个答案以及 Alan McBee 的下一个答案是有效的。对于我的情况,我认为这个答案更接近于给出我想要的行为(基类是一次性的,子类也是一次性的,但实际上并没有自己的“一次性”资源)。我在这里找到了类似的方法:gregbeech.com/blogs/tech/archive/2007/03/07/…
    【解决方案2】:

    我在基类和子类中都有用于实现 IDisposable 的 sn-ps。你可能想要一个子类。

    我认为我从 MSDN 刷了大部分代码。

    这是基类 IDisposable 的代码(不是您想要的):

    #region IDisposable Members
    // Track whether Dispose has been called.
    private bool _disposed = false;
    
    // Implement IDisposable.
    // Do not make this method virtual.
    // A derived class should not be able to override this method.
    public void Dispose()
    {
        Dispose(true);
        // Take yourself off the Finalization queue 
        // to prevent finalization code for this object
        // from executing a second time.
        GC.SuppressFinalize(this);
    }
    
    // Dispose(bool disposing) executes in two distinct scenarios.
    // If disposing equals true, the method has been called directly
    // or indirectly by a user's code. Managed and unmanaged resources
    // can be disposed.
    // If disposing equals false, the method has been called by the 
    // runtime from inside the finalizer and you should not reference 
    // other objects. Only unmanaged resources can be disposed.
    protected virtual void Dispose(bool disposing)
    {
        // Check to see if Dispose has already been called.
        if (!this._disposed)
        {
            // If disposing equals true, dispose all managed 
            // and unmanaged resources.
            if (disposing)
            {
                // TODO: Dispose managed resources.
    
            }
            // Release unmanaged resources. If disposing is false, 
            // only the following code is executed.
            // TODO: Release unmanaged resources
    
            // Note that this is not thread safe.
            // Another thread could start disposing the object
            // after the managed resources are disposed,
            // but before the disposed flag is set to true.
            // If thread safety is necessary, it must be
            // implemented by the client.
        }
        _disposed = true;
    }
    
    // Use C# destructor syntax for finalization code.
    // This destructor will run only if the Dispose method 
    // does not get called.
    // It gives your base class the opportunity to finalize.
    // Do not provide destructors in types derived from this class.
    ~Program()
    {
        // Do not re-create Dispose clean-up code here.
        // Calling Dispose(false) is optimal in terms of
        // readability and maintainability.
        Dispose(false);
    }
    #endregion
    

    这是我在子类中使用的代码(这是你想要的代码):

    #region IDisposable Members
    // Track whether Dispose has been called.
    private bool _disposed = false;
    
    // Design pattern for a derived class.
    // Note that this derived class inherently implements the 
    // IDisposable interface because it is implemented in the base class.
    // This derived class does not have a Finalize method
    // or a Dispose method without parameters because it inherits 
    // them from the base class.
    protected override void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            try
            {
                if (disposing)
                {
                    // Release the managed resources you added in
                    // this derived class here.
                    // TODO: Dispose managed resources.
                }
                // Release the native unmanaged resources you added
                // in this derived class here.
                // TODO: Release unmanaged resources.
                _disposed = true;
            }
            finally
            {
                // Call Dispose on your base class.
                base.Dispose(disposing);
            }
        }
    }
    #endregion
    

    寻找TODO: 标记。

    【讨论】:

      猜你喜欢
      • 2012-03-28
      • 2012-05-05
      • 2011-12-20
      • 1970-01-01
      • 1970-01-01
      • 2012-03-17
      • 2013-02-02
      • 1970-01-01
      • 2013-09-29
      相关资源
      最近更新 更多