为什么Dispose()是非虚拟的?

Sbo*_*odd 31 .net c# dispose idisposable

我是C#的新手,如果这是一个明显的问题,请道歉.

MSDN Dispose示例中,它们定义的Dispose方法是非虚拟的.这是为什么?这对我来说似乎很奇怪 - 我希望IDisposable的子类具有自己的非托管资源,它只会覆盖Dispose并在自己的方法底部调用base.Dispose().

谢谢!

Cyl*_*Cat 14

典型的用法是Dispose()被重载,具有公共的非虚拟Dispose()方法和虚拟的受保护的Dispose(bool).公共Dispose()方法调用Dispose(true),子类可以使用此受保护的虚拟方法释放自己的resorces,并为父类调用base.Dispose(true).

如果拥有公共Dispose()方法的类也实现了终结器,则终结器调用Dispose(false),指示在垃圾回收期间调用了受保护的Dispose(bool)方法.

如果有终结器,则公共Dispose()方法还负责调用GC.SuppressFinalize()以确保终结器不再处于活动状态,并且永远不会被调用.这允许垃圾收集器正常处理类.具有活动终结器的类通常仅在gen0,gen1和gen2清理后作为最后的手段收集.

  • 您的描述非常精确.但有一句话,`public Dispose()`方法应该__always__调用`GC.SuppressFinalize()`当该声明类型被__not__密封时. (6认同)
  • @Cylon:[第3/3部分]最后一点,我注意到"密封",因为只有当你的班级被密封时,你才能确定没有人从你的班级继承,从而增加了终结者.我希望这能解决问题. (4认同)
  • @Cylon:[Part 1/3]我同意任何带有终结器的类都应该有一个`Dispose`方法.但是,这并不意味着终结器和`Dispose`应该在同一个类中.这并不成立,因为你并不总是设计这两个类(想想框架设计者).在`System.IO.Stream`查找实例.它实现了`IDisposable`,但没有终结器.但是,`FileStream`实际上确实实现了终结器.Stream调用`SuppressFinalize`,即使它本身没有实现终结器. (3认同)
  • @Cylon:[Part 2/3]当`Stream`没有时,`FileStream`必须在它的`Dispose(bool)`方法中调用`SuppressFinalize`,这不是那么糟糕,但这并不遵循'处置模式'.更糟糕的是在`Stream`类上实现一个空的终结器,因为当开发人员忘记处理它们的对象时,这会增加保留在堆上的对象的变化.谈论糟糕的设计:`System.ComponentModel.Component`实际上有一个空的终结器,当实例没有正确处理时,这会导致各种麻烦(我已经看到OOM因为这个而被抛出). (3认同)
  • @Steven:值得注意的是,在框架设计者中,密封类是规则而不是例外,因为很难提前评估用户可能使用(并可能破坏)您的类的所有不同方式派生类.因此,你不需要特殊的理由来封印课程.见http://blogs.msdn.com/b/ericlippert/archive/2004/01/22/61803.aspx (3认同)

Ste*_*ven 8

这当然不是一个明显的问题.这种模式特别选择,因为它在以下场景中运行良好:

  • 没有终结器的类.
  • 有终结器的类.
  • 可以继承的类.

虽然虚拟Dispose()方法可以在类不需要最终化的场景中工作,但是如果您确实需要最终化,它在场景中不能很好地工作,因为这些类型通常需要两种类型的清理.即:托管清理和非托管清理.出于这个原因,该Dispose(bool)方法被引入模式中.它可以防止重复清理代码(其他答案中缺少这一点),因为该Dispose()方法通常会清理托管和非托管资源,而终结器只能清理非托管资源.

  • 我认为这是对实际问题的更直接的回答,问题是"为什么Dispose()应该是非虚拟的?" 进一步澄清可能是为了解释,对于"两种类型的清理",未来的派生类可能需要最终化(因此它自己的`Dispose(bool)`),即使基类没有.如果发生这种情况并且如果基类没有提供`虚拟Dispose(bool)`那么派生类实现就不能调用`base.Dispose(bool)`,因此它不能正确处理基类. (2认同)

Rob*_*vey 5

虽然接口中的方法在通常意义上不是"虚拟的",但它们仍然可以在继承它们的类中实现.这显然是C#语言内置的一种便利,允许在不需要virtual关键字的情况下创建接口方法,并且无需override关键字即可实现方法.

因此,虽然IDisposable接口包含一个Dispose()方法,但它virtual前面没有关键字,也不必override在继承类中使用关键字来实现它.

通常的Dispose模式是在您自己的类中实现Dispose,然后在基类中调用Dispose以便它可以释放它拥有的资源,依此类推.

类型的Dispose方法应该释放它拥有的所有资源.它还应该通过调用其父类型的Dispose方法释放其基类型所拥有的所有资源.父类型的Dispose方法应释放它拥有的所有资源,然后调用其父类型的Dispose方法,通过基类型的层次结构传播此模式.

http://msdn.microsoft.com/en-us/library/fs2xkftw.aspx

  • +1它归结为这样一个事实:当你可能不知道如何最好地清理父母的资源时,你不想依赖孩子来清理东西. (3认同)
  • @Grzenio Nope,如果子类重写Dispose,则基类的Dispose方法将不再可访问,因此父级的资源永远不会被释放. (2认同)