【问题标题】:Implementing IDisposable correctly on parent classes in C#在 C# 中的父类上正确实现 IDisposable
【发布时间】:2016-03-22 13:20:28
【问题描述】:

我有一个实现 C# SerialPort 的类,它曾经看起来像这样:

public class AsyncSerial : IDisposable
{
    SerialPort newPort; //Parameters declared in my constructor
    //Constructor and other methods

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if(disposing)
        {
            this.Close();
            this.Dispose();
        }
    }
}

这不会在代码分析中引发任何警告(我从MSDN 获得代码作为如何正确执行此操作的示例)。

因为我只打算声明一个 SerialPort,所以我想我会让我的班级成为 SerialPort 的孩子,但现在我收到了似乎无法修复的警告。

public class AsyncSerial : SerialPort
{
    //Constructor and other methods

    public new void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected new virtual void Dispose(bool disposing)
    {
        if(disposing)
        {
            this.Close();
            this.Dispose();
        }
    }
}

代码警告说 dispose 方法应该是new,因为它们隐藏了成员,我这样做了,但我也得到了:

“警告 CA1063 确保 'AsyncSerial.Dispose()' 被声明为公共和密封”

将其密封意味着必须将其标记为override(否则我会遇到编译器错误),使其覆盖意味着它可以是新的,所以我最终得到:

错误 CS0506“AsyncSerial.Dispose()”:无法覆盖继承的成员“Component.Dispose()”,因为它未标记为虚拟、抽象或覆盖

我不知道在父类中使用 IDisposable 的类上实现处置的“正确”方法。我发现的每一个例子都只适合以IDisposable 为基础,但是让我的类

public class AsyncSerial : SerialPort, IDisposable
{
    //code
}

给我一​​个代码分析警告,因为SerialPort 已经实现了IDisposable

我是否应该取消有关确保“AsyncSerial.Dispose()”被声明为公共和密封的警告,或者是否有正确的方法来执行此操作而不给出代码分析警告。

【问题讨论】:

  • 嵌入 SerialPort 很好,但您只需要在 Dispose() 方法中处理它,不需要其他任何东西,也不需要一次性模式。毫不犹豫地使用密封。继承也很好,但是您需要 覆盖 Dispose(bool) 而不是继承 IDisposable。因为您似乎没有其他不必要的一次性用品。

标签: c# design-patterns idisposable


【解决方案1】:

Dispose 模式旨在允许派生类型以一致的方式添加处置逻辑,而无需考虑父类型是否具有公共 Dispose 方法或显式实现 IDisposable.Dispose。从遵循该模式的类型派生的类型应该简单地覆盖 Dispose(bool),而不管父类如何实现使用公共方法或 IDisposable.Dispose() 的显式实现。

尽管Dispose 模式的设计基于一个有缺陷的假设,即公开暴露的可继承类型的对象通常需要直接合并终结器(而不是将非托管资源封装在私有类型的私有实例中,其目的是清理这些资源),编译器生成的 C++/CLI(可能还有其他语言)中的清理逻辑依赖于该模式,因此最好将它与其他人可以使用的可继承类一起使用。

【讨论】:

    【解决方案2】:

    您不需要在子类上声明 public void Dispose() 方法,因为它已经从基类继承(编译器无论如何都不会让您这样做,除非您使用 new 隐藏基实现关键字)。

    如果您不打算处理特定于该子类的任何内容,也不需要覆盖基类的 protected virtual void Dispose(bool)

    如果您的子类中有一个IDisposable 引用,那么您应该覆盖您的基类的方法:

    public class AsyncSerial : SerialPort, IDisposable
    {
        // SomeClass implements IDisposable
        private SomeClass _disposableInstance;
    
        // ...
    
        protected override void Dispose(bool disposing)
        {
            if(disposing)
            {
                if(_disposableInstance != null)
                    _disposableInstance.Dispose();
            }
    
            // Call the base Dispose, to release resources on the base class.
            base.Dipose(disposing);
        }
    }
    

    【讨论】:

      【解决方案3】:

      如果有的话,您的子类应该覆盖 Dispose(bool disposing) - 这就是拥有该方法的全部意义,真的。

      然而,我怀疑基类无论如何都会做出正确的调用,所以你不需要做任何事情,除非你有额外的资源要释放,而 没有在@987654322 中发布@。如果是这种情况,请在Dispose(bool disposing)

      protected override void Dispose(bool disposing)
      {
          // Allow the base class to release resources
          base.Dispose(disposing);
          // Release any extra resources here 
      }
      

      请注意,当您的两个 Dispose 重载相互调用时,您当前的实现将导致 StackOverflowException。

      【讨论】:

      • 好的,我会确保处理我在子类中创建的任何东西(尽管我认为没有任何东西)。基类处理了其他所有东西,我把它复杂化了,让自己感到困惑。谢谢你
      猜你喜欢
      • 2011-01-30
      • 1970-01-01
      • 2011-05-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-22
      • 2017-01-21
      相关资源
      最近更新 更多