【问题标题】:How much work in the Dispose method?Dispose 方法的工作量是多少?
【发布时间】:2012-01-19 20:20:25
【问题描述】:

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

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

【问题讨论】:

  • 经过下面的所有讨论,我认为我对 Dispose 的想法可能有点过于严格,有时在 Dispose 期间有一点工作流程是可以的。我仍将专注于保持严格的精益 Dispose 方法,但我现在更愿意在绝对需要时进行更多处理。感谢所有反馈,感谢大家的宝贵时间。

标签: c# dispose correctness


【解决方案1】:

我错了吗?

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

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

using (Html.BeginForm())
{

}

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

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

【讨论】:

  • 嗯,有时这是有道理的,因为您知道添加“”字符串很快而且不会例外。我关心的是访问 SQL 或文件,您很有可能会失去连接并导致未处理的异常,或者当所有对象想要做的是 Dispose 时只是做了太多工作。看看我在说什么?我希望 Dispose 快速流畅。不要在文件流超时时挂断。
  • @BobbyCannon,是的,当然,对于这种情况,您应该真正限制该方法所做的工作量。
  • @BobbyCannon:即使对象以难以清理的方式操纵外部实体,Dispose 方法仍应清理它。不要责怪 Dispose 方法做的太多——要么责怪让实体一开始就处于那种状态的人,要么认为清理很烦人但很有必要。
  • @supercat 为什么 Dispose 的工作是清理这种状态? Dispose 方法的工作是清理对象占用的资源。也许我对我定义为“资源”的东西很严格,但我认为任何东西都在运行。我不认为 SQL 或文件 DATA 是“资源”。还要记住,我并不是说我是对的,而是对你们的想法感到好奇。就像 shanselman 说的那样,我有坚定的信念,但很轻率。
  • @BobbyCannon:如果一个对象有责任确保状态被清理,并且它的Dispose 方法没有清理状态,那么状态将如何被清理?
【解决方案2】:

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

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

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

【讨论】:

  • 我不同意。对象的构造函数应该只使对象进入实例化状态。构造函数中不应进行任何处理或工作。如果您需要其他工作,它应该在成员方法中。写入日志对于构造函数来说工作量太大。我总是做这个纸质测试。我可以在没有大量处理时间的情况下循环实例化 1,000,000 这个对象吗?如果答案是否定的,那么就有问题了。我对 dispose 也有同样的感觉。
  • @BobbyCannon 我不同意。在某些情况下,在构造函数中做一些工作(当然你不会访问数据库)是很好的,记录器的例子实际上是一个很好的例子。了解 Microsoft 如何在 Tracer 类中完成这项工作:entlib.codeplex.com/SourceControl/changeset/view/90009#529980 +1 for the logger
  • @BobbyCannon 看来你已经知道你想要的答案了,只是在寻找确认。
  • @cadrell0 是的,我想我知道我会考虑的答案。我正在寻找的是看看其他人是否可以说服我。如果它使我成为更好的开发人员,我喜欢被证明是错误的。所以我只是好奇别人的想法。
【解决方案3】:

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

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

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

在构造函数中,我一直认为你应该只做实例化对象绝对必要的事情。

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

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

其实让我们稍微分解一下...

从 GC 调用处理到 Finalizer

当您实现 IDisposable 模式(不是接口、模式、终结器等等)时,您实际上是在说您的对象有一个其他人都不知道的非托管资源。这意味着您已经 PInvoked 对 Win32 的 CreateFile 的调用,或者您可能调用了 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 方法中应该做什么和不应该做什么的断言实际上应该被改写以应用于任何由终结器调用的东西。事实上,这通常在称为 Dispose 的方法中完成,这是一个约定问题,即 IDisposable 模式,但这些问题很容易在 Finalizer 使用的其他方法中存在。

【讨论】:

    【解决方案4】:

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

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

    【讨论】:

      【解决方案5】:

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

      【讨论】:

      • 我同意。我认为几乎每条规则都会有“例外”。这意味着你几乎不应该看到它。当您这样做时,可能应该对为什么要完成这项工作进行冗长的解释(评论)。
      猜你喜欢
      • 2017-04-04
      • 1970-01-01
      • 1970-01-01
      • 2011-08-23
      • 2013-12-21
      • 2020-03-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多