Dispose(bool disposing)模式被破坏

 

介绍

.NET出现以来,最令人困惑的事情之一是Dispose()(来自IDisposable接口)与终结器之间的关系。

最初的想法

最初的想法很简单:垃圾收集器为我们完成内存和资源的清理,因此我们不需要手动管理内存和资源。

实际上,事情变得更加复杂。当我们需要垃圾收集器运行时,它可能不会运行(正如它所说的,它是不确定的”)。同样的,static字段使对象保持活动状态,并且我们的代码可以与本地代码交互,并且我们需要对对象和内存生命期进行更多控制。

这使得终结器在垃圾收集器运行时释放外部资源,并通过Dispose()方法尽快释放任何资源,而无需等待垃圾收集器运行。这就产生了滚雪球的效果,我们改变整个类层次结构来实现模式,通常我们想要控制当资源释放(Dispose()方法和IDisposable接口)以及控制终结器,因为任何非托管数据都需要会被释放即使我们(或我们代码的用户)不调用Dispose()

原始解决方案——Dispose模式

原始的解决方案是Dispose模式。这并不容易,因为它包括:

  • 具有终结器(调用Dispose(false););
  • 具有Dispose()(调用Dispose(true);GC.SuppressFinalize(this););
  • 具有一个Dispose(bool disposing);重载,其决定了根据disposing真正要做的事情。此重载可能是虚拟的,也可能不是虚拟的,从而增加了模式的复杂性。

Dispose模式的雪球效应

必须决定一个物体是否需要是一次性的,这本身就是一个问题。而且Dispose模式与基类和框架的交互确实很差。

如果框架对象可能需要可预测的销毁,则意味着我们需要一个Dispose()或类似的方法。但是,作为框架或基类,这也意味着子类中的对象可能具有不受管理的数据,这意味着我们需要终结器。

现在,整个模式需要在任何可能有处理非托管数据的子类的基类上使用。

所以,与其简单的说:

public abstract class MyBaseClass:
  IDisposable
{
  public virtual void Dispose()
  {
  }
}

我们需要改为以下内容:

public abstract class MyBaseClass:
  IDisposable
{
  ~MyBaseClass()
  {
    Dispose(false);
  }

  public void Dispose()
  {
    GC.SupressFinalize(this);
    Dispose(true);
  }

  protected virtual void Dispose(bool disposing)
  {
  }
}

请务必注意,在代码的第一段中,Dispose()virtual。在第二个中,Dispose() 不应该virtual,而Dispose(bool disposing)需要是virtual...,并且也不应该是公共的,因为它不应该由用户代码调用。

什么是disposing

当我第一次看到带有“dispose”参数的Dispose()方法时,我真的很困惑。已经命名为Dispose的方法中的disposing是什么?

我真的认为,如果将Dispose模式命名为Release模式,并且我们有一个isFromManualDispose参数,那么事情就不会那么混乱了。它将仍然是一个有问题的模式,但是要理解该论点的含义会容易一些。

为什么这个模式被破坏了?

老实说,破坏太强了,但是这是我想要吸引读者注意的东西。这很糟糕,因为它取决于太多的方法和概念,而且也令人困惑。这也很令人困惑。即使“它工作”时正确实现,它:

  • 对于新开发者来说很难;
  • 意味着即使整个框架不使用任何不安全的代码,任何框架类都需要使用Dispose(bool)来处理可能的不安全数据;
  • 意味着继承那些类的任何人都需要知道如何处理可恶的” disposing参数;
  • 意味着违反单一责任原则。一个框架类(或只是任何基类)不应处理所有这些以防万一需,但是一个子类需要处理。

解决方案:SafeHandles

一段时间之后,Microsoft注意到了这种不良模式,并试图对其进行修复。那是我们得到SafeHandle的时间。

拥有SafeHandle 的整个想法是,我们的类应该只处理托管内存,或者如果确实需要,则处理安全句柄,而不是让我们自己的类处理托管和非托管内存。它将具有终结器,并且将真正管理非托管数据的生命周期。

我确实认为Microsoft在一开始就对其进行了记录,但是当我试图找到他们的好例子时,我才发现最新的文档,该文档被完全破坏了。他们解释为什么SafeHandle是好的,并帮助我们避免坏的Disposable模式,但随后显示一个使用SafeHandle和实现了Dispose模式的类,却没有真正的好处!

阅读文档后,似乎我们现在在硬模式之上有了一个新模式。但这完全是错误的。新模式取代了旧的硬模式。不要添加到它。

对于那些好奇的人,我指的是此页面

在该页面的源代码中,甚至包含以下注释:

// No finalizer is needed. The finalizer on SafeHandle
// will clean up the MySafeFileHandle instance,
// if it hasn't already been disposed.
// Howerver, there may be a need for a subclass to
// introduce a finalizer, so Dispose is properly implemented here.
[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
protected virtual void Dispose(bool disposing)
  • 关于子类引入终结器的注释是Dispose模式的另一个问题。有人认为,在需要终结器之前我们不应该添加终结器,但是如果基类决定使用终结器,则可能会发生两次处置。我们应该避免所有这些混乱;
  • 在该示例中,单词“Howerver”的拼写错误。编辑者,请不要在本文中修复此问题;
  • 他们说在这里适当地实现了Dispose”,但实际上却忘记了检查disposing参数。当disposing错误时,他们不支持调用_handle.Dispose()

模式

SafeHandle创建的目的是简化模式。使用SafeHandle时,我们不必担心整个Dispose模式。相反,我们只需要知道我们是否正在实现Dispose()。只是简单的Dispose(),而不是那么古怪的Dispose(bool disposing)

然后,如果我们正在处理Windows句柄,则使用适当的安全句柄;如果从未处理过某个对象并收集垃圾,则SafeHandle将为我们完成工作。

实际上,这是新的和改进的模式的基础,即使我们没有处理非托管内存或数据的SafeHandl

不使用SafeHandle时,新模式究竟是什么?

简单规则:

  • Public类不应具有析构函数或Dispose(bool)。如果是一次性的,则只使用标准Dispose()实现IDisposable
  • 如果他们使用任何可能需要析构函数的数据,则应使用帮助程序类来保存该数据。正是这样的SafeHandle:帮助程序类保存数据并为您处理析构函数(仅此而已)。

在某种程度上,仅此而已。

如何实现一个帮助程序类?

帮助程序类将需要有一个析构函数,并且可能需要一个析构函数Dispose()以允许资源的早期释放。但是这些帮助程序类可以被密封,并避免任何逻辑来处理托管+非托管数据。它们的存在的唯一目的是处理非托管数据的释放,因此无需进行检查。他们不应该做任何其他事情,因为那将是主要类的工作。他们只是简单的帮手。

重新分析问题

  • Dispose模式:

带有Dispose(bool)的类,一个调用Dispose(false)的终结器,一个调用Dispose(true)Dispose()重载,而且bool disposing使许多开发商不知道是怎么回事,即使他们的类从来不使用非托管的数据。

  • 新模式:

只是一个简单的IDisposable接口实现,如果类需要确定性清除,如果类可以继承,那么它应该是virtual。如果该类使用了任何非托管数据,则使用一个帮助程序类(对于所有非托管数据都可以相同,例如SafeHandle)。就这样。没有disposing这样的参数,也没有奇怪的实现。

默认情况下,当基类和子类不保存非托管数据时,它们将更加简单。他们仍将能够保留非托管数据(如果需要),但是会将这些数据的发布委托给助手类。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值