IDisposable 基类拥有托管一次性资源,在子类中做什么?

发布于 2024-07-25 03:39:27 字数 3201 浏览 6 评论 0原文

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

那么在子类中呢? 子类如何在其公共方法和属性中知道它们已被释放? 起初我认为子类不必有任何特殊的东西(比如实现它们自己的 Dispose 版本),因为需要处理的东西仅在基类中(让我们假设子类不会添加任何需要处理的数据)需要显式处置)并且基类的 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 。

您会

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

看到基类拥有并管理 PerformanceCounter 对象(这是一次性的 重写 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 级别的“已处置”成员变量?

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

我错过了这艘船吗?

I have a base class that owns a managed disposable resource (.NET PerformanceCounter). I understand about implementing IDisposable on the class so that I can explicitly call Dispose on the resource. From the examples I have seen, people typically use a private boolean member variable "disposed" and set it to true inside of Dispose. Later, if there is an attempt to access a public method or property, an ObjectDisposedException is raised if "disposed" is true.

What about in the subclasses? How would the subclasses, in their public methods and properties, know that that they had been disposed? At first I thought that the subclasses would not have to anything special (like implement their own version of Dispose) since the thing that needs to be disposed is only in the base class (let's assume that the subclasses won't be adding any data that needs to be explicitly disposed) and the base class' Dispose should handle that. Should the subclasses override the base class' virtual Dispose method solely for the purpose of setting its own "disposed" member variable?

Here is a very stripped-down version of the class hierarchy in question.

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
}

In our system, ReadableCounter and WritableCounter are the only subclasses of BaseCounter and they are only subclassed to one more level via a code generation processes. The additional subclassing level only addes a specific name so that it becomes possible to create objects that correspond directly to named counters (e.g. if there is a counter that is used to count the number of widgets produced, it ends up being encapsulated in a WidgetCounter class. WidgetCounter contains the knowledge (really, just the counter name as a string) to allow the "WidgetCounter" performance counter to be created.

Only the code-generated classes are used directly by developers, so we would have something like this:

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

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

So, you see that the base class owns and manages the PerformanceCounter object (which is disposable) while the subclasses use the PerformanceCounter.

If I have code like this:

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

How could WritableCounter know, in Increment, that it had been disposed? Should ReadableCoutner and WritableCounter simply override the BaseCounter's

protected virtual void Dispose(bool disposing)

something like this:

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

simply to set a ReadableCounter/WritableCounter-level "disposed" member variable?

How about if the base class (BaseCounter) declared disposed as protected (or made it a protected property)? That way, the subclasses could refer to it rather than adding a Dispose method simply for the purpose of remembering that Dispose had happened.

Am I missing the boat on this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

自控 2024-08-01 03:39:27

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

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

I have seen some disposable classes with a public IsDisposed property. You could do that and check it in your sub-classes.

Another thing I've done is a generic protected 'Validate' method that all sub-class methods call (and could override). If it returns, all is well, otherwise it might throw. That would insulate your sub-classes from the disposable innards altogether.

枉心 2024-08-01 03:39:27

我有用于在基类和子类中实现 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: 标记。

I have snippets that I use for implementing IDisposable, both in the base class and in the subclasses. You'd probably want the one for the subclass.

I swiped most of this code from MSDN, I think.

Here's the code for the base class IDisposable (not the one you want):

#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

And here's the code I use in the subclasses (this is the code you want):

#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

Look for the TODO: marks.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文