我知道c#中析构函数和终结符的含义和用法的区别。
然而,通常“我应该......”的回答是“不要使用析构函数,而是使用MSDN中所示的dispose模式”。 Eric Lippert writes非常strongly反对使用析构函数。
但是,“模式”主张将析构函数编写为~T() { Dispose(false); }
。陈述的原因是,如果程序员忘记拨打Dispose()
,则会调用“后备”。当然,这忽略了这样一个事实,即终结者在他们的行动中是不确定的,甚至可能永远不会跑。
因此:
如果我使用处理模式,我是否还应该提供析构函数?顺便说一下,我只处理托管资源(例如实体框架DataContext
)。
如果我提供了一个析构函数:如果我的类派生自IDisposable
,它可能已经提供了析构函数,那么我是否应该提供一个析构函数?我认为在这种情况下永远不会写析构函数,但是文档说它无论如何都会自动调用基类的析构函数。
答案 0 :(得分:18)
我实际上不会回答你的两个问题,但我会就以下问题提出意见:
说明理由是,如果程序员忘记拨打
Dispose()
,则会调用“后备”。
如果是要求,方法的调用者传递一个非空字符串,那么如果它们传递null,你完全有权抛出异常,对吗?来电者违反了合同;这是异常行为,所以你抛出异常。你不会想,哦,来电者“忘了”传递一个有效的论点,我想我会接受糟糕的输入和士兵。这样做有效地将方法的契约从“null是不可接受的并且将产生异常”改为“null是可接受的并且被视为空字符串”。
如果是要求用户在完成后调用Dispose,而他们没有,那么这与调用者在调用方法时未能履行合同没有什么不同。调用者未能满足要求,因此使程序崩溃。如果析构函数遇到非处置对象,则抛出一个信息性异常。正如调用者很快就会知道将错误的参数传递给方法会受到伤害一样,他们也会知道未能处理对象也会造成伤害。
显式处理对象必要或者不是。如果有必要,请确保用户这样做。否则就是隐瞒他们的错误。
答案 1 :(得分:12)
如果我使用处理模式,我还应该提供析构函数吗?顺便说一下,我只处理托管资源(例如Entity Framework DataContext)。
在这种情况下,没有。原因是,当您的类被GC捕获时,所有这些对象也将由GC处理。在这种情况下,没有理由添加析构函数的开销。
这是IDisposable
复杂性的一部分 - 根据使用情况,应该应该不仅仅是标准实现。在这种情况下,您将封装实现IDisposable
的资源。因此,允许您的用户(间接)处理这些资源很重要,但您不需要处理析构函数,因为您没有直接“拥有”的非托管资源。如果您需要更多详细信息,我会在Part 3 of my series on IDisposable中介绍此内容。
如果我提供了一个析构函数:如果我的类派生自一个IDisposable,它可能已经提供了一个析构函数,那么我也应该提供一个析构函数吗?我认为在这种情况下永远不会写析构函数,但是文档说它无论如何都会自动调用基类的析构函数。
在这种情况下,基类应该公开protected virtual void Dispose(bool disposing)
形式的受保护方法。您可以将资源清理逻辑放在那里,因为基类析构函数会为您处理对此方法的调用。有关详细信息,请参阅Part 2 of my series on IDisposable。
答案 2 :(得分:2)
如果您正在编写一个类,则不能强制使用该类的每个人都遵循预期的IDisposable模式。这就是你需要析构函数回退的原因。
即使“每个人”都是“只是你”,你也是人,有时会犯错误。
答案 3 :(得分:2)
很难在这个问题中添加一些尚未被这里的好答案所触及的问题。
然后我会尝试替代MSDN上提倡的配置模式。我从来没有真正喜欢Dispose(bool)
方法,所以我认为这种模式is better 如果你肯定需要析构函数:
public class BetterDisposableClass : IDisposable {
public void Dispose() {
CleanUpManagedResources();
CleanUpNativeResources();
GC.SuppressFinalize(this);
}
protected virtual void CleanUpManagedResources() {
// ...
}
protected virtual void CleanUpNativeResources() {
// ...
}
~BetterDisposableClass() {
CleanUpNativeResources();
}
}
但是,既然您已经发现自己确实不需要,那么您的模式是much simpler:
public class ManagedDisposable : IDisposable {
// ...
public virtual void Dispose() {
_otherDisposable.Dispose();
}
IDisposable _otherDisposable;
}
答案 4 :(得分:-1)
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();
}
}