c#析构函数:dispose;图案“;以及最佳实践
本文关键字:最佳 图案 析构函数 dispose | 更新日期: 2023-09-27 18:14:28
我知道c#中析构函数和finalizer在含义和使用上的区别。
然而,对于"我应该…"的回答通常是"不要使用析构函数,而是使用MSDN中显示的dispose模式"。Eric Lippert撰文强烈反对不必要地使用析构函数。
但是,这种"模式"主张将析构函数写成~T() { Dispose(false); }
。说明的原因是它是一个"回退",在程序员忘记调用Dispose()
的情况下调用它。当然,这忽略了一个事实,即最终确定者在操作中是不确定的,甚至可能永远不会运行。
因此:
-
如果我使用dispose模式,我是否也应该提供析构函数?顺便说一句,我只是在处理托管资源(例如实体框架
DataContext
(。 -
如果我提供了一个析构函数:如果我的类是从
IDisposable
派生的,它可能已经提供了析构函数,那么我应该也提供一个吗?我认为在这种情况下永远不会编写析构函数,但是文档说它无论如何都会自动调用基类的析构函数。
我不会真正回答你的两个问题,但我会提供一个意见:
说明的原因是它是一个"回退",在程序员忘记调用
Dispose()
的情况下调用它。
如果要求方法的调用方传递非null字符串,那么如果它们传递null,那么您完全有权抛出异常,对吧?打电话的人违反了合同;这是一种异常行为,所以你抛出了一个异常。你不会认为,哦,调用者"忘记"传递了一个有效的参数,我想我会接受错误的输入并继续。这样做实际上是将方法的契约从"null是不可接受的,并将产生异常"更改为"null是可接受的并被视为空字符串"。
如果要求用户在完成Dispose时调用Dispose,而他们没有,那么这与调用方在调用方法时未能履行约定没有什么不同。调用方未能满足要求,因此使其程序崩溃。如果析构函数遇到未处理的对象,则让它抛出一个信息异常。正如调用方很快了解到将坏参数传递给方法会造成伤害一样,他们也会了解到未能处理对象也会造成伤害。
显式处理对象是必需的,或者不是。如果有必要,请确保用户这样做。否则就是隐藏他们的错误。
如果我使用dispose模式,我是否也应该提供析构函数?顺便说一句,我只是在处理托管资源(例如EntityFrameworkDataContext(。
在这种情况下,没有。原因是,当你的类被GC捕获时,所有这些对象也将由GC处理。在这种情况下,没有理由增加析构函数的开销。
这是IDisposable
复杂性的一部分——根据使用情况,应该比标准实现更多。在本例中,您正在封装一个实现IDisposable
的资源。因此,允许用户(间接(处置这些资源很重要,但您不需要处理析构函数,因为没有您直接"拥有"的非托管资源。如果您想了解更多详细信息,我将在IDisposable系列的第3部分中介绍这一点。
如果我提供了一个析构函数:如果我的类是从IDisposable派生的,它可能已经提供了析构函数,那么我应该也提供一个吗?我认为在这种情况下永远不会编写析构函数,但是文档说它无论如何都会自动调用基类的析构函数。
在这种情况下,基类应该公开一个形式为protected virtual void Dispose(bool disposing)
的受保护方法。您可以将资源清理逻辑放在那里,因为基类析构函数为您处理对该方法的调用。有关详细信息,请参阅我的IDisposable系列文章的第2部分。
如果您正在编写一个类,则不能强制使用该类的每个人都遵循预期的IDisposable模式。这就是为什么你需要析构函数回退。
即使"每个人"都是"只有你",你也是人,有时也会犯错误。
很难为这个问题添加一些尚未被这里的好答案所触及的内容。
然后,我将尝试给出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;
}
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();
}
}