你应该实现IDisposable.Dispose(),以便它永远不会抛出?
对于C ++(析构函数)中的等价机制,建议是通常不应抛出任何异常。 这主要是因为通过这样做你可能会终止你的过程,这很少是一个好策略。
在.NET中的等效方案中...
...你的过程不会立即终止。 但是,由于.NET不会将第一个异常替换为第二个异常,因此会丢失信息。 因此,调用堆栈上方的catch块将永远不会看到第一个异常。 但是,人们通常对第一个异常更感兴趣,因为这通常会给出更好的线索,说明为什么事情会出错。
由于.NET缺乏一种机制来检测代码是否正在执行,而异常处于挂起状态,因此IDisposable的实现方式似乎只有两种选择:
那么,这是两个邪恶中较小的一个? 有没有更好的办法?
编辑 :澄清,我不是说积极抛出异常从Dispose()或不,我说的是让由Dispose()调用的方法抛出的异常传播出Dispose()或不是,例如:
using System;
using System.Net.Sockets;
public sealed class NntpClient : IDisposable
{
private TcpClient tcpClient;
public NntpClient(string hostname, int port)
{
this.tcpClient = new TcpClient(hostname, port);
}
public void Dispose()
{
// Should we implement like this or leave away the try-catch?
try
{
this.tcpClient.Close(); // Let's assume that this might throw
}
catch
{
}
}
}
我认为在这种情况下吞咽是两个弊端中较小的一个,因为最好提高原始Exception
- 警告: 除非可能未完全处置本身是非常重要的(也许如果一个TransactionScope
不能处置,因为这可能表明回滚失败)。
在这里看到更多的想法 - 包括一个包装/扩展方法的想法:
using(var foo = GetDodgyDisposableObject().Wrap()) {
foo.BaseObject.SomeMethod();
foo.BaseObject.SomeOtherMethod(); // etc
} // now exits properly even if Dispose() throws
当然,你也可以做一些奇怪的事情,你用原始和第二个( Dispose()
)异常重新抛出一个复合异常 - 但是要考虑到:你可能有多个using
块......它很快就变得难以管理。 事实上,最初的例外是有趣的。
框架设计指南(第二版)将其作为(§9.4.1):
AVOID从Dispose(bool)中抛出一个异常,除非在包含进程已被破坏的关键情况下(泄漏,不一致的共享状态等)。
评论[编辑]:
Dispose(bool)
从终结器中抛出是个坏主意,并且会阻止其他对象被终结。 我的观点:从Dispose中逃脱出来的例外应该只是那些像在指南中那样的灾难,因为它足以造成灾难性的后果,使得当前进程中不可能有更多可靠的功能。
Dispose
应该被设计来完成它的目的,处理对象。 这项任务是安全的,大部分时间不会抛出异常 。 如果您发现自己从Dispose
抛出异常,您应该考虑两次,看看您是否在做太多的事情。 除此之外,我认为Dispose
应该像所有其他方法一样对待:处理是否可以对其进行处理,如果不能处理则让其冒泡。
编辑:对于指定的例子,我会编写代码,以便我的代码不会导致异常,但清除TcpClient
可能会导致异常,这应该是有效的传播在我看来(或作为一个更多的处理和rethrow泛型异常,就像任何方法一样):
public void Dispose() {
if (tcpClient != null)
tcpClient.Close();
}
然而,就像任何方法一样,如果你知道tcpClient.Close()
可能会抛出一个应该被忽略的异常(无关紧要),或者应该由另一个异常对象来表示,你可能想要捕获它。
上一篇: Should you implement IDisposable.Dispose() so that it never throws?
下一篇: If a generic collection is instantiated to contain iDisposable items, do the items get disposed?