dispose_Dispose(布尔处理)模式已损坏

dispose

介绍 (Introduction)

Since .NET appeared, one of the most confusing things was the relationship between Dispose() (from the IDisposable interface) and finalizers.

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

创见 (Original Idea)

The original idea was very simple: The Garbage Collector does the memory and resource cleanup for us, so we don't need to manage memory and resources manually.

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

In practice, things got much more complicated. The Garbage Collector might not run when we need it to run (as it is said, it is "non-deterministic"). Also, static fields keep objects alive and our code can interact with native code, and we need more control on objects and memory lifetime.

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

This justified having finalizers to release external resources when the Garbage Collector ran, and also Dispose() methods to release any resource "as soon as possible" without waiting for the Garbage Collector to run. And that created a snowball effect, where we change entire class hierarchies to implement a hard pattern, as usually we want to have control when resources are freed (the Dispose() method and the IDisposable interface) and also a finalizer, as any "unmanaged" data needs to be released even if we (or users of our code) don't invoke Dispose().

这有理由让终结在运行Garbage Collector时释放外部资源,并且还拥有Dispose()方法以“尽快”释放任何资源而无需等待Garbage Collector运行。 这就产生了滚雪球效应,我们在其中更改整个类层次结构以实现硬模式,通常情况下,我们希望控制何时释放资源( Dispose()方法和IDisposable接口)以及终结器,如任何“非托管”对象一样。即使我们(或代码的用户)不调用Dispose()也需要释放数据。

原始解决方案-处置模式 (The Original Solution - Dispose Pattern)

The original solution is the Dispose pattern. It is not really easy, as it consists of:

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

  • having a finalizer (calling Dispose(false););

    具有终结器(调用Dispose(false); );

  • having a Dispose() (calling Dispose(true); and GC.SuppressFinalize(this););

    具有Dispose() (调用Dispose(true);GC.SuppressFinalize(this); );

  • having an overloaded Dispose(bool disposing); which decided what to really do according to disposing. This overload could be virtual or not, increasing the complexity of the pattern.

    具有超载的Dispose(bool disposing); 根据disposing决定真正要做什么。 此重载可能是虚拟的,也可能不是虚拟的,从而增加了模式的复杂性。

处置模式的“雪球”效应 (The "Snowball" Effect of the Dispose Pattern)

Having to decide if an object needs to be disposable or not is, in itself, problematic. And the Dispose pattern interacts really badly with base-classes and frameworks.

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

If a framework object might ever need "predictable" destruction, that means we need a Dispose() or similar method. But, as a framework or base class, that also means objects from sub-classes might possibly have unmanaged data, meaning we "need" the finalizer.

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

Now, the entire pattern needs to be used on any base class that can possibly have a sub-class dealing with unmanaged data.

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

So, instead of having something as simple as:

因此,与其简单地像这样:

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

We need to have something like the following instead:

我们需要改为以下内容:

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

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

  protected virtual void Dispose(bool disposing)
  {
  }
}

It is important to notice that in the first block of code, Dispose() was virtual. In the second, Dispose() isn't supposed be virtual and, instead, Dispose(bool disposing) needs to be virtual... and also not-public, as it is not supposed to be invoked by user code.

重要的是要注意,在第一段代码中, Dispose()virtual 。 在第二篇中, Dispose() 不应该virtual ,相反, Dispose(bool disposing)应该是virtual ...而且也不是公共的,因为它不应该由用户代码调用。

什么是“处置”? (What is "disposing"?)

When I first saw Dispose() with a "disposing" parameter, I got really confused. What is "disposing" in a method already named "Dispose"?

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

I really think that if the Dispose pattern was named Release pattern and we had an "isFromManualDispose" argument, things would be less confusing. It would still be a problematic pattern, but it would be a little easier to understand what the argument means.

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

为什么图案断了? (Why Is the Pattern Broken?)

"Broken" is too strong, to be honest, but I wanted something that grabbed the reader's attention. It is bad because it depends on too many methods and concepts, and it's also confusing. Even if "it works" when implemented correctly, it:

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

  • is hard for new developers;

    对于新开发者来说很难;
  • means any framework class needs to have a Dispose(bool) to deal with possibly "unsafe" data even when the entire framework doesn't use any unsafe code;

    意味着即使整个框架不使用任何不安全的代码,任何框架类都需要具有Dispose(bool)来处理可能的“不安全”数据;

  • means that anybody inheriting those classes needs to know what to do with that "hateful" disposing argument;

    意味着继承那些类的任何人都需要知道如何disposing “可恶的” disposing参数;

  • means it breaks the Single Responsibility Principle. A framework class (or just any base class) is not supposed to deal with all of that "just in case" a sub-class needs it.

    表示违反单一责任原则 。 框架类(或只是任何基类)不应处理所有“子类”需要的“情况”。

解决方案:SafeHandles (The Solution: SafeHandles)

Sometime later, Microsoft noticed the bad pattern and tried to fix it. That's when we got SafeHandles.

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

The entire idea of having SafeHandles is that, instead of having our own classes dealing with "managed and unmanaged memory", our classes should just deal with managed memory or, if really needed, deal with a "safe handle", which is the object that will have a finalizer and will really manage the lifetime of the unmanaged data.

使用SafeHandle的整个想法是,与其让我们自己的类处理“托管和非托管内存”, SafeHandle让我们的类处理托管内存,或者,如果确实需要,则处理“安全句柄”,即具有终结器的对象,并将真正管理非托管数据的生命周期。

I really think Microsoft documented it right in the beginning, but when I tried to find their good example, I just found the latest document, which is completely "busted". They explain why SafeHandles are good and help us avoid the bad Disposable pattern but then show a class that uses a SafeHandle and also implements the Dispose pattern for no real benefit!

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

After reading the document, it seems that now we have a new pattern on top of the hard pattern. But that is just plain wrong. The new pattern came to replace the old, and hard, pattern. Not to add to it.

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

For those who are curious, I am referring to this page.

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

In that page, on the source code, there's even this comment:

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

// 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)

  • The comment about a subclass introducing a finalizer is another issue with the Dispose pattern. Some people argue that we shouldn't add a finalizer until we need it, but then if the base class ever decides to use a finalizer, we might have double-disposes happening. We should avoid all that confusion;

    关于子类引入终结器的评论是Dispose模式的另一个问题。 有人认为,在我们需要终结器之前不应该添加终结器,但是如果基类决定使用终结器,则可能会发生两次处置。 我们应该避免所有这些混乱;

  • The word "Howerver" is misspelled in that sample. Editors, please don't fix it here in the article;

    在该示例中,单词“ Howerver”的拼写错误。 编辑者,请不要在本文中修复此问题;
  • They say "Dispose is properly implemented here", but they actually forgot to to check the disposing argument. They were not supposed to call _handle.Dispose() when disposing is false.

    他们说,“处置正确这里实现”,但他们居然忘了检查disposing的参数。 如果disposing为false,则不应调用_handle.Dispose()

“新”模式 (The "new" Pattern)

SafeHandles were created with the purpose of simplifying the pattern. When using SafeHandles, we should not worry about the entire Dispose pattern. Instead, we should just need to know if we are implementing Dispose() or not. Just the simple Dispose(), not that weird Dispose(bool disposing).

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

Then, if we are dealing with Windows Handles, we use the appropriate safe-handles and, if an object is never disposed and gets Garbage Collected, the SafeHandles do the work for us.

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

This, actually, is the base for a new and improved pattern, even when we don't have SafeHandles for the kind of unmanaged memory or data we are dealing with.

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

不使用SafeHandles时,新模式究竟是什么? (What Exactly Is the New Pattern When Not Using SafeHandles?)

Simple rules:

简单规则:

  • Public classes should not have a destructor or a Dispose(bool). If they are disposable, they just implement IDisposable with the standard Dispose();

    Public类不应具有析构函数或Dispose(bool) 。 如果它们是一次性的,则只需使用标准Dispose()实现IDisposable

  • If they use any data that might need a destructor, they should use a helper class to hold that data. That's exactly what SafeHandles are: Helper classes that hold the data and deal with the destructor for you (and nothing else).

    如果他们使用任何可能需要析构函数的数据,则应使用帮助程序类来保存该数据。 这就是SafeHandle的确切含义:帮助程序类,用于保存数据并为您处理析构函数(仅此而已)。

In a way, that's all.

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

如何实现一个助手类? (How to Implement One of the Helper Classes?)

The helper classes will need to have a destructor, and possibly a Dispose() to allow for an early release of the resources. But those helper classes can possibly be sealed and avoid any logic to deal with managed + unmanaged data. They exist with the sole purpose of dealing with the release of unmanaged data, so there's no need to check for that. They should not do anything else, as that would be the work of the main class. They are just simple helpers.

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

重新分析问题 (Reanalyzing the Problem)

  • Dispose pattern:

    配置方式:

    Class with Dispose(bool), a finalizer that calls Dispose(false), an overloaded Dispose() that calls Dispose(true), and that bool disposing makes many developers wonder what is really going on, even if their classes never use unmanaged data.

    带有Dispose(bool) ,一个终结器,该终结器调用Dispose(false) ,一个重载的Dispose()调用Dispose(true) ,并且bool disposing使许多开发人员怀疑实际发生了什么,即使他们的类从不使用非托管数据。

  • New pattern:

    新模式:

    Just a simple implementation of the IDisposable interface if the class needs deterministic cleanup, which should be virtual if the class can be inherited. If any unmanaged data is used by the class, a helper class (which can be the same for all unmanaged data, like a SafeHandle) is used. That's all. No "disposing" argument and no odd implementations because of such an argument.

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

    By default, base classes and sub-classes will be much simpler when they don't hold unmanaged data. They will still be able to hold unmanaged data if needed, but will delegate the "release" of that data to a helper class.

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

翻译自: https://www.codeproject.com/Articles/5266619/The-Dispose-bool-disposing-Pattern-is-Broken

dispose

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值