C# 为什么Dispose()应该是非虚拟的?

C# 为什么Dispose()应该是非虚拟的?,c#,.net,dispose,idisposable,C#,.net,Dispose,Idisposable,我是C#的新手,如果这是一个明显的问题,我深表歉意 在中,它们定义的Dispose方法是非虚拟的。为什么呢?这对我来说似乎很奇怪——我希望IDisposable的子类拥有自己的非托管资源,它只会覆盖Dispose并在自己方法的底部调用base.Dispose() 谢谢 尽管接口中的方法在通常意义上不是“虚拟的”,但它们仍然可以在继承它们的类中实现。这显然是C语言中内置的一种便利,允许在不需要virtual关键字的情况下创建接口方法,以及在不需要override关键字的情况下实现方法 因此,尽管I

我是C#的新手,如果这是一个明显的问题,我深表歉意

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


谢谢

尽管接口中的方法在通常意义上不是“虚拟的”,但它们仍然可以在继承它们的类中实现。这显然是C语言中内置的一种便利,允许在不需要
virtual
关键字的情况下创建接口方法,以及在不需要
override
关键字的情况下实现方法

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

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

类型的Dispose方法应释放 它拥有的所有资源。信息技术 还应该释放所有资源 通过调用 父类型的Dispose方法。这个 父类型的Dispose方法应 释放它拥有的所有资源并 依次调用其父类型的Dispose 方法,传播此模式 通过基本类型的层次结构


如果基类有需要在
Dispose()
时间清理的资源,则使用由继承类重写的虚拟
Dispose
方法可以防止释放这些资源,除非继承类专门调用
base的
Dispose
方法。更好的实现方法是让每个派生类实现
IDisposable

示例的Dispose()方法是非虚拟的原因是,在该示例中,它们接管了整个过程,并将虚拟Dispose(bool disposing)方法的子类留给重写。您会注意到,在这个示例中,它存储了一个布尔字段,以确保Dispose逻辑不会被调用两次(可能从IDisposable调用一次,从析构函数调用一次)。重写提供的虚拟方法的子类不必担心这种细微差别。这就是为什么示例中的主Dispose方法是非虚拟的。

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

如果拥有public Dispose()方法的类也实现了终结器,那么终结器将调用Dispose(false),这表示在垃圾收集期间调用了受保护的Dispose(bool)方法


如果存在终结器,则public Dispose()方法还负责调用GC.SuppressFinalize(),以确保终结器不再处于活动状态,并且永远不会被调用。这允许垃圾收集器正常处理该类。带有活动终结器的类通常只能在第0代、第1代和第2代清理之后作为最后手段进行收集。

我已经对dispose模式进行了非常详细的解释。本质上,您提供了一个
受保护的
重写方法,该方法对非托管资源更为健壮。

这当然不是一个明显的方法。之所以选择此模式,是因为它在以下场景中运行良好:

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

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

通过接口的调用始终是虚拟的,而不管是否为“正常”电话可以是直接的,也可以是虚拟的。如果实际执行处置工作的方法不是虚拟的,除非通过接口调用,那么无论何时类想要处置自己,它都必须确保将其自身引用强制转换为iDisposable并调用它

在模板代码中,父级和子级中的非虚拟Dispose函数应始终相同[仅调用Dispose(True)],因此无需重写它。所有工作都在虚拟处置(布尔)中完成

坦率地说,我认为在没有理由期望后代类直接持有非托管资源的情况下,使用Dispose模式有点愚蠢。在.net的早期,类通常需要直接保存非托管资源,但今天在大多数情况下,我看到直接实现Dispose()不会造成任何损失。如果未来的子类需要使用非托管资源,它可以而且通常应该将这些资源包装到它们自己的可终结对象中

另一方面,对于某些类型的方法,拥有一个非虚拟基类方法(其工作是链接到一个受保护的虚拟方法)可能会有好处,并且让虚拟方法被称为
Dispose(bool)
实际上并不比
VirtDispos差