代码之家  ›  专栏  ›  技术社区  ›  wageoghe

拥有托管可释放资源的IDisposable基类,在子类中该怎么做?

  •  0
  • wageoghe  · 技术社区  · 15 年前

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

    在子类中呢?在它们的公共方法和属性中,子类如何知道它们已被释放?首先,我认为子类不需要任何特殊的东西(比如实现它们自己的Dispose版本),因为需要释放的东西只在基类中(假设子类不会添加任何需要显式释放的数据),而基类的Dispose应该处理它。子类是否应仅为了设置自己的“disposed”成员变量而重写基类的“virtual 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;
    

    可写计数器如何逐渐知道它已被处理?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方法。

    我错过了这条船吗?

    2 回复  |  直到 15 年前
        1
  •  0
  •   n8wrl    15 年前

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

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

        2
  •  0
  •   Alan McBee    15 年前

    我在基类和子类中都有用于实现IDisposable的代码片段。你可能想要子类的那个。

    我想,我从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: 标志。

    推荐文章