4

我在我的类中实现了 Finalize 和 Dispose,我在我的父类上实现了 IDisposable 并在我的子类中覆盖了 Dispose(bool) 重载。我不确定

  1. 是否使用重复的 isDisposed 变量(因为它已经存在于基类中)?
  2. 是否也在子类中实现终结器?

这两件事都在此处给出的示例中完成-

http://guides.brucejmack.biz/CodeRules/FxCop/Docs/Rules/Usage/DisposeMethodsShouldCallBaseClassDispose.html

而这篇 MSDN 文章中的示例没有这两个中的任何一个 - http://msdn.microsoft.com/en-us/library/b1yfkh5e.aspx

而 MSDN 中的这个例子并不完整 - http://msdn.microsoft.com/en-us/library/ms182330.aspx

4

5 回答 5

6

终结器很少有用。您链接到的文档并不完全有帮助 - 它提供了以下相当循环的建议:

仅对需要终结的对象实施终结

这是提出问题的一个很好的例子,但它不是很有帮助。

在实践中,绝大多数时候你不需要终结器。(.NET 开发人员必须经历的学习曲线之一是发现在他们认为需要终结器的大多数地方,他们不需要。)您已将其标记为(除其他外)一个 WPF 问题,并且我会说在 UI 对象上放置终结器几乎总是一个错误。(因此,即使您处于需要终结器的异常情况之一,该工作也不属于与 WPF 相关的代码附近的任何地方。)

在大多数情况下,终结器看起来可能有用,但事实证明它们并非如此,因为当你的终结器运行时,它已经为时已晚,无法做任何有用的事情。

例如,尝试对对象引用的任何对象执行任何操作通常是一个坏主意,因为在终结器运行时,这些对象可能已经终结。(.NET 不保证终结器的运行顺序,因此您根本无法知道您引用的对象是否已经终结。)在终结器已经完成的对象上调用方法是个坏主意被运行。

如果您有某种方式知道某个对象肯定还没有被终结,那么使用它是安全的,但这是一个非常不寻常的情况。(......除非有问题的对象没有终结器,并且本身不使用可终结的资源。但在这种情况下,当你自己的对象消失时,它可能不是你实际上需要做任何事情的对象。)

终结器似乎有用的主要情况是互操作:例如,假设您正在使用 P/Invoke 调用一些非托管 API,并且该 API 会返回一个句柄。也许您需要调用其他一些 API 来关闭该句柄。由于这都是非托管的东西,.NET GC 不知道这些句柄是什么,你的工作是确保它们被清理,此时终结器是合理的......除了在实践中,它几乎总是最好的用于SafeHandle该场景。

在实践中,我发现自己使用终结器的唯一地方是 a) 旨在调查 GC 功能的实验,以及 b) 旨在发现特定对象在系统中如何使用的诊断代码。两种代码都不应该最终投入生产。

所以你是否需要“在子类中实现终结器”的答案是:如果你需要问,那么答案是否定的。

至于是否复制标志......其他答案在这里提供了相互矛盾的建议。要点是 1)您确实需要调用 baseDispose和 2)您Dispose需要是幂等的。(即,它是否被调用一次、两次、5 次、100 次都没有关系——如果它被多次调用,它不应该抱怨。)你可以随意实现它——布尔标志是一种方法,但我经常发现null在我的Dispose方法中设置某些字段就足够了,此时不需要单独的布尔标志 - 你可以说它Dispose已经被调用,因为你已经将这些字段设置为null.

那里的许多指导IDisposable都非常无用,因为它解决了您需要终结器的情况,但这实际上是一个非常不寻常的情况。这意味着很多人编写的IDisposable实现比必要的复杂得多。在实践中,大多数类在jpierson 链接到的文章中都属于 Stephen Cleary 所称的“级别 1”类别。对于这些,您不需要所有使大多数示例混乱的GC.KeepAliveGC.SuppressFinalize和东西。Dispose(bool)大多数时候,生活实际上要简单得多,正如克利里对这些“1 级”类型的建议所表明的那样。

于 2010-12-14T13:08:00.067 回答
2

需要复制

如果您在子类中没有任何清理,只需调用base.Dispose(),如果有一些类级别的清理,请在调用base.Dispose(). 您需要将这两个类的状态分开,因此IsDisposed每个类都应该有一个布尔值。这样您就可以在需要时添加清理代码。

当您将一个类确定为IDisposable时,您只需告诉GC我正在处理它的清理程序,您应该SuppressFinilize在这个类GC上将其从队列中删除。除非你打电话给一个班级GC.SupressFinalize(this)没有什么特别的事情发生。IDisposable因此,如果您按照我提到的那样实现它,则不需要 a ,Finilizer因为您只是告诉GC不要最终确定它。

于 2010-12-14T12:40:59.777 回答
1

实现 IDisposable 的正确方法取决于您的类是否拥有任何非托管资源。实现 IDisposable 的确切方法仍然不是所有开发人员都同意的,并且像Stephen Cleary这样的一些人对一次性范式有强烈的意见。

请参阅:实施 Finalize 和 Dispose 以清理非托管资源

IDisposable 接口的文档也简要说明了这一点,这篇文章指出了一些相同的事情,但也在 MSDN 上。

至于基类中是否需要重复的布尔字段“isDisposed”。看起来这主要只是一个有用的约定,当子类本身可能添加需要处理的额外非托管资源时可以使用它。由于 Dispose 被声明为虚拟的,因此在子类实例上调用 Dispose 总是会导致首先调用该类的 Dispose 方法,然后调用 base.Dispose 作为最后一步,以便有机会清除继承层次结构中的每个级别。因此,我可能会将其总结为,如果您的子类在基类所拥有的资源之上有额外的非托管资源,那么您可能最好拥有自己的布尔 isDisposed 字段来跟踪它的 Dispose 方法内部的事务处理,但作为 Ian在他的回答中提到,

于 2010-12-14T12:42:28.730 回答
0

1)无需复制

2) 实现终结器将有助于处置未明确处置的项目。但不能保证。这是一个很好的做法。

于 2010-12-14T12:21:18.713 回答
0

仅当对象包含有关需要清理的内容的信息时才实现终结器,并且此信息以某种形式而不是对需要清理的其他对象的对象引用(例如,存储为 Int32 的文件句柄)。如果一个类实现了一个终结器,它不应该持有对任何其他不需要清理的对象的强对象引用。如果它包含其他引用,则负责清理的部分应使用终结器拆分为自己的对象,并且主对象应包含对此的引用。然后主对象不应该有终结器。

如果基类的目的是支持一个,派生类应该只有终结器。如果类的目的不以终结器为中心,那么允许派生类添加终结器就没有多大意义,因为派生类几乎肯定不应该(即使它们需要添加非托管资源,它们也应该将资源放入他们自己的类,只是持有对它的引用)。

于 2010-12-14T15:54:26.913 回答