Dispose方法有多少工作?

时间:2012-01-19 20:20:25

标签: c# dispose correctness

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

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

5 个答案:

答案 0 :(得分:4)

  

我错了吗?

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

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

using (Html.BeginForm())
{

}

和Dispose方法所做的就是渲染一个结束</form>标记。因此,您可以看到人们可能对该模式具有创造性,并且在没有特定情况的情况下很难得出结论。

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

答案 1 :(得分:2)

“这取决于”。我们在谈论什么样的数据库/文件访问?比如说你的一次性物体是某种记录器,你可以用下面的模式

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

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

答案 2 :(得分:1)

  

在Dispose方法中应该完成多少工作?

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

当你定义一个调用Dispose方法的终结器时,真的没什么可担心的。已经提到的IDisposable接口的类似使用/滥用已经是其他(即using (Html.BeginForm()))能够执行任何动作。通常,这可以大大降低代码复杂性并防止编码器意外忘记执行某些关闭操作。向下(或向上)的一个是code executes a little differently inside a finally block

  

在构造函数中,我总是采取的立场是,您应该只执行实例化对象所必需的操作。

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

  

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

实际上让我们稍微打破一下......

从GC调用处理到终结器

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

通常,您不要在儿童身上调用Dispose方法。您不应期望任何子对象有效。简单地触摸子对象的成员可能会意外地“复活”或resurrect it

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

从IDisposable.Dispose()方法处置

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

  

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

当使用终结器中的dispose方法时,你是绝对正确的。你有关于你应该和不应该在Dispose方法中做什么的断言实际上应该改写为适用于Finalizer调用的任何东西。这通常在一个名为Dispose的方法中完成,这是一个常规问题,即IDisposable模式,但这些问题很容易存在于Finalizer使用的其他方法中。

答案 3 :(得分:0)

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

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

答案 4 :(得分:0)

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