如果您实现IDisposable.Dispose(),所以它不会抛出?如果您、抛出、IDisposable、Dispose

2023-09-02 11:56:03 作者:此昵称正在审核中°

有关在C ++中(析构函数)相当的机制,该建议是,它应该通常不会引发任何异常。这主要是因为这样做你可能会终止你的进程,这是只有很少一个很好的策略。

在同等情况下的.NET ...

在第一个异常被抛出 在finally块被执行的第一个异常的结果 在finally块调用Dispose()方法 的Dispose()方法会引发第二个异常

...您的进程不会立即终止。但是,你失去的信息,因为.NET unceremoneously替换第二个第一例外。因此,一个catch块地方调用堆栈将永远不会看到的第一个例外。然而,通常更感兴趣的是第一个例外,因为这通常提供更好的线索,为什么事情开始出问题。

由于.NET缺少一种机制来检测是否code的同时有异常等待处理被执行,似乎真的有只有两个选择IDisposable的是如何实现的:

始终吞下发生内部的Dispose()所有异常。不好,你也可能最终吞咽OutOfMemoryException异常,ExecutionEngineException等,这些我通常宁愿让他们的时候没有出现其他异常已经在等待拆除的过程。 让所有的异常传播出去处置()。不好,你可能会失去一个问题的根本原因的信息,请参阅上面。

所以,这是较小的两害?有没有更好的办法?

亿联银行 民营银行与传统银行有哪些区别

修改:为了澄清,我不是在谈论正在从处置抛出异常()或没有,我说的是让抛出的方法调用的Dispose异常()传播出去处置()或不是,例如:

 使用系统;
使用的System.Net.Sockets;

公共密封类NntpClient:IDisposable的
{
    私人TcpClient的TcpClient的;

    公共NntpClient(字符串主机名,INT端口)
    {
        this.tcpClient =新的TcpClient(主机名,端口);
    }

    公共无效的Dispose()
    {
        //如果我们实行这样或离开远在try-catch?
        尝试
        {
            this.tcpClient.Close(); //假设,这可能会引发
        }
        抓住
        {
        }
    }
}
 

解决方案

我认为,吞咽在这种情况下的两害较轻的,因为它是更好地提高的原的异常 - 警告:除非,也许是没有干净处理本身是pretty的该死的关键(也许如果的TransactionScope 无法处理,因为这可能表明回滚失败)。

请参阅这里更多这方面的想法 - 包括一个包装/扩展方法的想法:

 使用(VAR富= GetDodgyDisposableObject()。总结()){
   foo.BaseObject.SomeMethod();
   foo.BaseObject.SomeOtherMethod(); // 等等
} //现在退出正确,即使Dispose()方法抛出
 

当然,你也可以做一些古怪,你有再抛出一个复合异常原始和第二(的Dispose())的例外 - 但想想:你可以有多个使用块......它会很快变得难以管理。在现实中,原来的例外是有趣的。

For the equivalent mechanism in C++ (the destructor), the advice is that it should usually not throw any exceptions. This is mainly because by doing so you might terminate your process, which is only very rarely a good strategy.

In the equivalent scenario in .NET ...

A first exception is thrown A finally block is executed as a result of the first exception The finally block calls a Dispose() method The Dispose() method throws a second exception

... your process does not terminate immediately. However, you lose information because .NET unceremoneously replaces the first exception with the second one. A catch block somewhere up the call stack will therefore never see the first exception. However, one is usually more interested in the first exception because that normally gives better clues as to why things started to go wrong.

Since .NET lacks a mechanism to detect whether code is being executed while an exception is pending, it seems there are really only two choices how IDisposable can be implemented:

Always swallow all exceptions that occur inside Dispose(). Not good as you might also end up swallowing OutOfMemoryException, ExecutionEngineException, etc. which I'd usually rather let tear down the process when they occur without another exception already pending. Let all exceptions propagate out of Dispose(). Not good as you might lose information about the root cause of a problem, see above.

So, which is the lesser of the two evils? Is there a better way?

EDIT: To clarify, I'm not talking about actively throwing exceptions from Dispose() or not, I'm talking about letting exceptions thrown by methods called by Dispose() propagate out of Dispose() or not, for example:

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
        {
        }
    }
}

解决方案

I would argue that swallowing is the lesser of the two evils in this scenario, as it is better to raise the original Exception - caveat: unless, perhaps the failure to cleanly dispose is itself pretty darned critical (perhaps if a TransactionScope couldn't dispose, since that could indicate a rollback failure).

See here for more thoughts on this - including a wrapper/extension method idea:

using(var foo = GetDodgyDisposableObject().Wrap()) {
   foo.BaseObject.SomeMethod();
   foo.BaseObject.SomeOtherMethod(); // etc
} // now exits properly even if Dispose() throws

Of course, you could also do some oddity where you re-throw a composite exception with both the original and second (Dispose()) exception - but think: you could have multiple using blocks... it would quickly become unmanageable. In reality, the original exception is the interesting one.

 
精彩推荐
图片推荐