C# 析构函数:处置“模式”和最佳实践

发布于 2024-12-15 05:49:05 字数 897 浏览 0 评论 0原文

我知道 C# 中析构函数和终结器的含义和使用差异。

但是,通常“我应该……”的答案是“不要使用析构函数,而是使用处置模式,如 MSDN"。埃里克·利珀特写道< /a> 相当 强烈反对使用不必要的析构函数。

但是,该“模式”提倡编写析构函数,例如 ~T() { Dispose(false); }。声明的原因是,它是一个“后备”,在程序员忘记调用 Dispose() 时调用。当然,这忽略了这样一个事实:终结器在其操作中是不确定的,甚至可能永远不会运行。

因此:

  1. 如果我使用 dispose 模式,我还应该提供析构函数吗?顺便说一句,我仅处置托管资源(例如实体框架 DataContext)。

  2. 如果我确实提供了析构函数:如果我的类派生自 IDisposable,它可能已经提供了析构函数,那么我也应该提供一个吗?我认为在这种情况下永远不会编写析构函数,但是文档说它无论如何都会自动调用基类的析构函数。

I know the difference in meaning and use of destructors and finalisers in c#.

However, typically the answer to a "should I ..." is answered by "don't use destructors, rather use the dispose pattern as shown in MSDN". Eric Lippert writes quite strongly against using destructors unnecessarily.

But, that "pattern" advocates writing a destructor as such ~T() { Dispose(false); }. The stated reason is that it is a "fallback" which is called in case the programmer forgets to call Dispose(). Of course this ignores the fact that finalisers are indeterminate in their operation, and may never even run.

Hence:

  1. If I use the dispose pattern, should I also provide the destructor? Incidentally, I am only disposing managed resources (the Entity Framework DataContext for example).

  2. If I do provide a destructor: if my class is derived from an IDisposable, which may already provide a destructor, then should I provide one too? I thought that one never writes a destructor in such a case, however docs say that it will call the base class' destructor automatically anyway.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

止于盛夏 2024-12-22 05:49:05

我实际上不会回答你的两个问题,但我会提供以下意见:

声明的原因是,它是一个“后备”,在程序员忘记调用 Dispose() 时调用。

如果方法的调用者要求传递一个非空字符串,那么您完全有权利在它们传递 null 时抛出异常,对吧?调用者违反约定;这是异常行为,因此您抛出异常。你不会认为,哦,调用者“忘记”传递一个有效的参数,我想我会接受错误的输入并继续前进。例如,这样做可以有效地将方法的约定从“null 不可接受,将产生异常”更改为“null 可接受并被视为空字符串”。

如果要求用户在完成后调用 Dispose,但他们没有这样做,那么这与调用者在调用方法时未能履行约定没有什么不同。调用者未能满足要求,因此使他们的程序崩溃。如果析构函数遇到未释放的对象,则抛出一个信息性异常。正如调用者很快就会知道向方法传递错误的参数会造成伤害一样,他们也会知道未能处理对象也会造成伤害。

显式处置该对象要么是必要的,要么不是。如果有必要,请确保用户这样做。否则就是隐藏他们的错误

I won't actually answer your two questions, but I will provide an opinion on:

The stated reason is that it is a "fallback" which is called in case the programmer forgets to call Dispose().

If it is a requirement that a caller of a method pass, say, a non-null string, then you are perfectly within your rights to throw an exception if they pass null, right? The caller violated the contract; that's exceptional behaviour so you throw an exception. You don't think, oh, the caller "forgot" to pass a valid argument, I think I'll accept the bad input and soldier on. Doing so is effectively changing the contract of the method from "null is unacceptable and will produce an exception" to "null is acceptable and is treated as an empty string", for example.

If it is a requirement that the user calls Dispose when they are done, and they did not, then that's no different than a caller failing to fulfill the contract when calling a method. The caller failed to fulfill a requirement, so crash their program. Have the destructor throw an informative exception if it encounters a non-disposed object. Just as callers quickly learn that passing bad arguments to a method hurts, they'll learn that failing to dispose your object hurts too.

Either explicitly disposing the object is necessary or it is not. If it is necessary, then make sure the user does so. Doing otherwise is concealing their bug.

女中豪杰 2024-12-22 05:49:05

如果我使用处置模式,我还应该提供析构函数吗?顺便说一句,我只处理托管资源(例如实体框架 DataContext)。

在这种情况下,不。原因是,当您的类被 GC 捕获时,所有这些对象也将由 GC 处理。在这种情况下没有理由增加析构函数的开销。

这是 IDisposable 复杂性的一部分 - 实际上应该比标准实现更多,具体取决于使用情况。在本例中,您将封装一个实现 IDisposable 的资源。因此,允许您的用户(间接)处置这些资源很重要,但您不需要处理析构函数,因为您没有直接“拥有”的非托管资源。我在我的 IDisposable 系列的第 3 部分中介绍了这一点如果您想了解更多详细信息。


如果我确实提供了析构函数:如果我的类派生自 IDisposable,而 IDisposable 可能已经提供了析构函数,那么我也应该提供一个吗?我认为在这种情况下人们永远不会编写析构函数,但是文档说它无论如何都会自动调用基类的析构函数。

在这种情况下,基类应该公开 protected virtual void Dispose(bool dispose) 形式的受保护方法。您可以将资源清理逻辑放在那里,因为基类析构函数会为您处理对此方法的调用。有关详细信息,请参阅我的 IDisposable 系列的第 2 部分。

If I use the dispose pattern, should I also provide the destructor? Incidentally, I am only disposing managed resources (the Entity Framework DataContext for example).

In this case, no. The reason is that, by the time your class is caught by the GC, all of those objects are also going to be handled by the GC. There is no reason to add the overhead of a destructor in this case.

This is part of the complexity of IDisposable - there really should be more than the standard implementation, depending on the usage. In this case, you're encapsulating a resource that implements IDisposable. As such, its important to allow your user to (indirectly) dispose those resources, but you don't need to handle the destructor, as there is no unmanaged resource you directly "own". I cover this in Part 3 of my series on IDisposable if you want more details.


if I do provide a destructor: if my class is derived from an IDisposable, which may already provide a destructor, then should I provide one too? I thought that one never writes a destructor in such a case, however docs say that it will call the base class' destructor automatically anyway.

In this case, the base class should expose a protected method of the form protected virtual void Dispose(bool disposing). You would put your resource cleanup logic there, as the base class destructor handles the call to this method for you. For details, see Part 2 of my series on IDisposable.

寂寞笑我太脆弱 2024-12-22 05:49:05

如果您正在编写一个类,则无法强制使用该类的每个人都遵循预期的 IDisposable 模式。这就是为什么你需要析构函数后备。

即使“每个人”都是“只有你”,你也是人,有时也会犯错误。

If you're writing a class, you can't force everyone that uses that class to follow the expected IDisposable pattern. That's why you need the destructor fallback.

Even if "everyone" is "just you", you are human and will sometimes make mistakes.

最笨的告白 2024-12-22 05:49:05

很难在这个问题上添加一些尚未被这里的精彩答案触及的内容。

然后我将尝试提供 MSDN 上提倡的处置模式的替代方案。我从来没有真正喜欢过 Dispose(bool) 方法,所以我认为这种模式 更好 如果你确实需要一个析构函数

public class BetterDisposableClass : IDisposable {

  public void Dispose() {
    CleanUpManagedResources();
    CleanUpNativeResources();
    GC.SuppressFinalize(this);
  }

  protected virtual void CleanUpManagedResources() { 
    // ...
  }
  protected virtual void CleanUpNativeResources() {
    // ...
  }

  ~BetterDisposableClass() {
    CleanUpNativeResources();
  }

}

但是,由于你已经发现你真的不需要一个,所以你的模式是 更简单

public class ManagedDisposable : IDisposable {

  // ...

  public virtual void Dispose() {
    _otherDisposable.Dispose();
  }

  IDisposable _otherDisposable;

}

It's very hard to add something to this question that hasn't been touched already by the great answers here.

I'll try then to give an alternative to the dispose pattern that's advocated on MSDN. I never really liked that Dispose(bool) method, so I think this pattern is better if you definitely need a destructor:

public class BetterDisposableClass : IDisposable {

  public void Dispose() {
    CleanUpManagedResources();
    CleanUpNativeResources();
    GC.SuppressFinalize(this);
  }

  protected virtual void CleanUpManagedResources() { 
    // ...
  }
  protected virtual void CleanUpNativeResources() {
    // ...
  }

  ~BetterDisposableClass() {
    CleanUpNativeResources();
  }

}

But, since you already found out that you really don't need one, your pattern is much simpler:

public class ManagedDisposable : IDisposable {

  // ...

  public virtual void Dispose() {
    _otherDisposable.Dispose();
  }

  IDisposable _otherDisposable;

}
风吹雨成花 2024-12-22 05:49:05
 Or if you know that your object that you are trying to dispose of Implements IDisposable
why not do something like this

StreamWriter streamWrt = null
try
{
streamWrt = new StreamWrite();
... do some code here
}
catch (Exception ex)
{
 Console.WriteLine(ex.Message)
}
Finally
{
  if (streamWrt != null)
  {
    ((IDisposable)streamWrt).Dispose();
  }
}
 Or if you know that your object that you are trying to dispose of Implements IDisposable
why not do something like this

StreamWriter streamWrt = null
try
{
streamWrt = new StreamWrite();
... do some code here
}
catch (Exception ex)
{
 Console.WriteLine(ex.Message)
}
Finally
{
  if (streamWrt != null)
  {
    ((IDisposable)streamWrt).Dispose();
  }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文