3

一个 Dispose 方法应该做多少工作?在构造函数中,我一直采取的立场是,你应该只做实例化对象绝对必要的事情。在这种情况下,我也一直采用这样的方法,即您应该只在处置时清理开放资源。关闭文件、释放内存、处置子一次性对象等。您不应该在 Dispose 方法中执行诸如触摸文件、访问数据库等冗长的过程。

我错了吗?只要您处理任何可能的异常以使它们不会从方法中冒出来,这些操作就可以吗?我只是不认为在 Dispose 中做很多事情是一个好主意。我想知道社区的想法。

4

5 回答 5

4

我错了吗?

不,你是对的。通常,Dispose 方法用于清理您的类可能已分配的非托管资源。

但这很难一概而论。在某些情况下,Dispose 方法仅用于确保执行某些操作。例如,在 ASP.NET MVC 中有 Html.BeginForm 帮助器,它的使用方式如下:

using (Html.BeginForm())
{

}

Dispose 方法所做的只是呈现一个结束</form>标记。因此,正如您所看到的,人们可能对这种模式很有创意,如果没有特定的场景,很难得出结论。

但在最常见的情况下,它用于释放非托管资源。

于 2012-01-19T20:21:59.833 回答
2

“这取决于”。我们在谈论什么样的数据库/文件访问?例如,假设您的一次性对象是某种记录器,并且您按以下模式使用它

using(Logger logger = new Logger())
{
    foo.PerformTask();
}

我认为 logger 在 Dispose 的构造函数“Log Completed”中写出“Log started”是完全可以接受的。

于 2012-01-19T20:34:43.687 回答
1

一个 Dispose 方法应该做多少工作?

这取决于,您是为了方便“使用”语句而实现 IDispose 接口,还是实现完整的 IDisposable 模式?在完全一次性模式的后一种情况下,如果您的“处置”参数为真(即您不在 GC 中),执行更复杂的操作仍然是可以接受的。

当您定义一个调用 Dispose 方法的终结器时,实际上并没有太多需要关注的问题。已经提到的 IDisposable 接口的类似使用/滥用(即using (Html.BeginForm()))能够执行任何操作。通常这可以大大降低代码复杂性并防止编码人员意外忘记执行某些关闭操作。不利(或有利)的一面是代码在 finally 块中的执行方式略有不同

在构造函数中,我一直采取的立场是,您应该只做实例化对象绝对必要的事情。

恕我直言,对象应该是有效的后期构造。所以如果你有很多工作要做来构建一些东西,那就这样吧。不要考虑所涉及的工作量,想想你的消费者是对象和它的可用性。构造后 Initialize() 方法很烂;)

在这种情况下,我也一直采用这样的方法,即您应该只在处置时清理开放资源。关闭文件、释放内存、处置子一次性对象等。您不应该在 Dispose 方法中执行诸如触摸文件、访问数据库等冗长的过程。

实际上,让我们分解一下...

从 GC 调用处理到 Finalizer

当您实现 IDisposable 模式(不是接口、模式、终结器和所有)时,您实际上是在说您的对象具有其他人不知道的非托管资源。这意味着您已经 PInvoked 对 Win32 的 CreateFile 的调用,或者您可能调用了 Marshal.AllocHGlobal 或类似的东西。本质上,您可能有一个 IntPtr 实例成员,您需要做一些事情来防止内存泄漏。当 disposing 参数为 false 时(即从 GC 线程上的终结器调用),这些是唯一应该做的事情。

通常,您不要对孩子调用 Dispose 方法。您不应期望任何子对象都是有效的。简单地触摸子对象的成员可能会意外地“复活”或复活它

因此,当您编写在从 Finalizer 调用的 Dispose 方法中执行的代码时,您必须小心。您正在 GC 线程上执行,而应用程序的其余部分正在等待您。您应该执行尽可能少的操作来释放非托管内存/资源并退出。永远不要抛出异常,如果您正在调用可能抛出的 API,您应该捕获任何引发的异常。将异常传播回 GC 将过早中止终结器线程,并且要终结的剩余对象将没有机会清理。

从 IDisposable.Dispose() 方法处理

正如我已经说过的,使用 Dispose 方法足够安全,并且可以安全地容纳任意数量的代码/进程。在这里您可以释放非托管资源、调用子对象的 dispose 方法、刷新和关闭文件等。我编写的大多数 Dispose 方法没有关联的 Finalizer,因此不遵循 IDisposable 模式,但它们实现 IDisposable 只是为了using声明的方便。

我错了吗?只要您处理任何可能的异常以使它们不会从方法中冒出来,这些操作就可以吗?我只是不认为在 Dispose 中做很多事情是一个好主意。我想知道社区的想法。

当从终结器中使用有问题的 dispose 方法时,您是绝对正确的。您对在 Dispose 方法中应该做什么和不应该做什么的断言实际上应该重新措辞以适用于终结器调用的任何内容。事实上,这通常在称为 Dispose 的方法中完成,这是一个约定问题,即 IDisposable 模式,但这些问题很容易存在于 Finalizer 使用的其他方法中。

于 2012-01-19T22:05:14.420 回答
0

你应该倾向于你已经得出的结论。但是,在某些情况下,您需要确保服务已停止,这可能包括为服务关闭记录消息或将当前运行时状态保存到数据存储等内容。这种类型的处置通常仅适用于具有应用程序范围的生活方式的事物,这意味着它们在应用程序运行的整个过程中都存在。因此,有些情况超出了预期的规范。与编写代码时应遵循的每条规则一样。

于 2012-01-19T21:27:53.807 回答
0

如果一个对象以某种方式对某些外部实体的状态做了一些事情,使它们对其更有用,但对其他人的用处不大,那么Dispose该对象的方法应该做任何必要的事情来将这些实体外部恢复到更普遍有用的状态. 如果希望避免让一个对象在其中做太多工作Dispose,那么应该设计该对象,以免任何外部实体处于难以清理的状态。

顺便说一句,微软喜欢使用术语“非托管资源”,并给出了例子,但从来没有真正提供一个好的定义。我建议,如果外部实体以对其他对象或实体有害的方式代表该对象改变其行为,并且该外部实体将继续改变其行为直到对象阻止它这样做。

于 2012-01-19T20:36:20.717 回答