我是C#的新手,如果这是一个明显的问题,请道歉。
在MSDN Dispose example中,他们定义的Dispose方法是非虚拟的。这是为什么?这对我来说似乎很奇怪 - 我希望IDisposable的子类具有自己的非托管资源,它只会覆盖Dispose并在它们自己的方法的底部调用base.Dispose()。
谢谢!
答案 0 :(得分:14)
典型的用法是Dispose()被重载,具有公共的非虚拟Dispose()方法和虚拟的受保护的Dispose(bool)。公共Dispose()方法调用Dispose(true),子类可以使用此受保护的虚方法释放自己的resorces,并为父类调用base.Dispose(true)。
如果拥有公共Dispose()方法的类也实现了终结器,则终结器调用Dispose(false),表示在垃圾回收期间调用了受保护的Dispose(bool)方法。
如果有终结器,那么公共Dispose()方法也负责调用GC.SuppressFinalize()以确保终结器不再处于活动状态,并且永远不会被调用。这允许垃圾收集器正常处理类。具有活动终结器的类通常仅在gen0,gen1和gen2清理后作为最后的手段收集。
答案 1 :(得分:8)
这当然不是一个明显的问题。这种模式特别选择,因为它在以下场景中运行良好:
虽然虚拟Dispose()
方法可以在类不需要最终化的场景中工作,但是如果您确实需要最终化,它在场景中不能很好地工作,因为这些类型通常需要两种类型的清理 - 起来。即:托管清理和非托管清理。出于这个原因,在模式中引入了Dispose(bool)
方法。它可以防止重复清理代码(其他答案中缺少这一点),因为Dispose()
方法通常会清理托管和非托管资源,而终结器只能清理非托管资源。
答案 2 :(得分:5)
虽然接口中的方法不是通常意义上的“虚拟”,但它们仍然可以在继承它们的类中实现。这显然是C#语言内置的一种便利,允许创建接口方法而无需virtual
关键字,并且无需override
关键字即可实现方法。
因此,尽管IDisposable
界面包含Dispose()
方法,但它前面没有virtual
关键字,您也不必使用override
继承类中的关键字来实现它。
通常的Dispose模式是在您自己的类中实现Dispose,然后在基类中调用Dispose以便它可以释放它拥有的资源,依此类推。
类型的Dispose方法应该释放 它拥有的所有资源。它 还应该释放所有资源 通过调用它的基类型来拥有它 父类型的Dispose方法。该 父类型的Dispose方法应该 释放它拥有的所有资源 反过来调用其父类型的Dispose 方法,传播这种模式 通过基础类型的层次结构。
答案 3 :(得分:3)
通过接口进行的呼叫始终是虚拟的,无论“正常”呼叫是直接呼叫还是虚拟呼叫。如果实际执行处理工作的方法不是虚拟的,除非通过接口调用,那么只要该类想要自行处理它,就必须确保将其自引用转换为iDisposable并调用它。
在模板代码中,非虚拟Dispose函数在父节点和子节点中始终是相同的[简单地调用Dispose(True)],因此永远不需要覆盖它。所有工作都在虚拟Dispose(布尔)中完成。
坦率地说,我认为在没有理由期望后代类直接持有非托管资源的情况下,使用Dispose模式有点愚蠢。在.net的早期阶段,类通常需要直接保存非托管资源,但是在大多数情况下,我认为直接实现Dispose()只会造成零损失。如果未来的后代类需要使用非托管资源,它可以并且通常应该将这些资源包装在它们自己的Finalizable对象中。
另一方面,对于某些类型的方法,使用非虚拟基类方法可能是有利的,该方法的作用是链接到受保护的虚方法,并且将虚方法称为Dispose(bool)
是真的不比VirtDispose()
差,即使提供的参数相当无用。例如,在某些情况下,对象的所有操作都可能需要由基类对象拥有的锁保护。让非虚基类Dispose
在调用虚方法之前获取锁定将使所有基类免于担心锁本身。
答案 4 :(得分:3)
Dispose
方法不应该是虚拟的,因为它不是模式实现一次性的扩展点。这意味着层次结构中的基本一次性类将为dispose创建顶级策略(算法),并将详细信息委托给另一个方法(Dispose(bool)
)。 此顶级策略是稳定的,不应被子类重写。如果允许子类覆盖它,它们可能不会调用算法的所有必要部分,这可能会使对象保留不一致的状态。
这类似于template method pattern,其中高级方法实现算法框架并将细节委托给其他可覆盖的方法。
作为旁注,我更倾向于此特定模式的another高级策略(仍然使用非虚拟Dispose
)。
答案 5 :(得分:2)
示例的Dispose()方法是非虚拟的原因是因为它们接管了该示例中的整个过程,并使用虚拟Dispose(bool disposing)方法保留子类来覆盖。您会注意到,在示例中,它存储了一个布尔字段,以确保Dispose逻辑不会被调用两次(可能一次来自IDisposable,一次来自析构函数)。覆盖提供的虚拟方法的子类不必担心这种细微差别。这就是为什么示例中的主Dispose方法是非虚拟的。
答案 6 :(得分:1)
我对配置模式here有一个非常详细的解释。从本质上讲,您提供了一个protected
方法来覆盖,这对于非托管资源来说更加健壮。
答案 7 :(得分:0)
如果基类具有需要在Dispose()
时间清理的资源,那么具有被继承类覆盖的虚拟Dispose
方法会阻止释放这些资源,除非继承类具体打电话给
base的Dispose
方法。实现它的更好方法是让每个派生类实现IDisposable
。