我刚接触C#,如果这是一个显而易见的问题,请见谅。
在MSDN释放范例中,他们定义的Dispose方法是非虚拟的。为什么呢?这对我来说似乎很奇怪 - 我本来希望一个IDisposable的子类如果有自己的非托管资源,只需要覆盖Dispose并在自己方法的底部调用base.Dispose()。
谢谢!
我刚接触C#,如果这是一个显而易见的问题,请见谅。
在MSDN释放范例中,他们定义的Dispose方法是非虚拟的。为什么呢?这对我来说似乎很奇怪 - 我本来希望一个IDisposable的子类如果有自己的非托管资源,只需要覆盖Dispose并在自己方法的底部调用base.Dispose()。
谢谢!
典型用法是Dispose()方法被重载,其中有一个公共的非虚拟Dispose()方法和一个虚拟的受保护的Dispose(bool)方法。公共的Dispose()方法调用Dispose(true),而子类可以使用这个受保护的虚拟方法来释放它们自己的资源,并为父类调用base.Dispose(true)。
如果拥有公共Dispose()方法的类也实现了finalizer,则finalizer将调用Dispose(false),表示在垃圾回收期间调用了受保护的Dispose(bool)方法。
如果存在finalizer,则公共Dispose()方法还负责调用GC.SuppressFinalize()以确保finalizer不再活动,并且永远不会被调用。这允许垃圾收集器正常处理该类。具有活动finalizer的类通常只在经过gen0、gen1和gen2清理后作为最后的手段被回收。
public Dispose()
方法应在声明类型未被封闭时__始终__调用GC.SuppressFinalize()
。 - StevenDispose
方法。然而,这并不意味着终结器和 Dispose
应该在同一个类中。这个观点不成立,因为你并不总是设计这两个类(想一想框架设计者)。例如,看一下 System.IO.Stream
。它实现了 IDisposable
接口,但没有终结器。然而,FileStream
确实实现了终结器。Stream 调用 SuppressFinalize
,即使它自己没有实现终结器。 - StevenStream
无法时,FileStream
必须在其Dispose(bool)
方法中调用SuppressFinalize
,这并不是很糟糕,但这不符合“处理模式”。更糟糕的是,在Stream
类上实现一个空的终结器,因为这会增加开发人员忘记处理对象时对象保留在堆上的风险。谈到糟糕的设计:System.ComponentModel.Component
实际上有一个空的终结器,当实例没有得到适当处理时,这会引起各种问题(我曾见过因此而抛出OOM)。 - Steven这绝对不是一件显而易见的事情。选择这种模式的原因是因为它在以下情况下表现良好:
虚拟的 Dispose()
方法可以在不需要终结的类中起作用,但在需要终结的类中却不太适用,因为这些类型通常需要两种类型的清理。即:受控清理和非受控清理。出于这个原因,该模式引入了 Dispose(bool)
方法。它可以防止清理代码的重复(其他答案中缺少此点),因为 Dispose()
方法通常会同时清理受控和非受控资源,而终结器只能清理非受控资源。
Dispose(bool)
),未来的派生类可能也需要。如果发生这种情况,并且基类没有提供virtual Dispose(bool)
,那么派生类实现无法调用base.Dispose(bool)
,因此无法正确处理基类的处理。 - bobFinalize
不是主要问题。更重要的因素是确保派生类可以以相同的方式添加 Dispose
逻辑,无论实现 IDisposable
的基本级别类是否公开公共 Dispose
方法(而不是显式实现 Dispose
)。如果 Foo1
显式实现 Dispose
,并且 Foo2
继承 Foo1
但也具有公共 Dispose
方法,则继承 Foo2
的 Foo3
应该覆盖哪个方法?说所有功能都应在受保护的虚拟方法中,并且所有公共方法都应链接到该方法... - supercatDispose()
时需要清理资源,则具有被继承类重写的虚拟Dispose
方法会防止这些资源被释放,除非继承类明确调用基类的Dispose
方法。更好的实现方式是让每个派生类实现IDisposable
。IDisposable
。使Dispose
虚拟化是可能的,但是派生类必须调用基类的Dispose
方法。 - SwDevMan81[CA1816] Change Dispose() to call GC.SuppressFinalize(object). This will prevent derived types that introduce a finalizer from needing to re-implement 'IDisposable' to call it.
这里是一个例子
class Base : IDisposable
{
public virtual void Dispose()
{
...
GC.SuppressFinalize(this);
}
}
public class Derived : Base
{
public override void Dispose() // <- still warns for CA1816
{
base.Dispose();
...
}
}