你应该实现IDisposable.Dispose(),以便它永远不会抛出?

对于C ++(析构函数)中的等价机制,建议是通常不应抛出任何异常。 这主要是因为通过这样做你可能会终止你的过程,这很少是一个好策略。

在.NET中的等效方案中...

  • 抛出第一个异常
  • 作为第一个异常的结果,finally块被执行
  • finally块调用Dispose()方法
  • Dispose()方法抛出第二个异常
  • ...你的过程不会立即终止。 但是,由于.NET不会将第一个异常替换为第二个异常,因此会丢失信息。 因此,调用堆栈上方的catch块将永远不会看到第一个异常。 但是,人们通常对第一个异常更感兴趣,因为这通常会给出更好的线索,说明为什么事情会出错。

    由于.NET缺乏一种机制来检测代码是否正在执行,而异常处于挂起状态,因此IDisposable的实现方式似乎只有两种选择:

  • 始终吞下Dispose()中发生的所有异常。 不好,因为您最终可能会吞下OutOfMemoryException,ExecutionEngineException等等,我通常宁愿让它们在发生时没有另一个异常已经挂起的进程拆除进程。
  • 让所有异常传播出Dispose()。 不好,因为您可能会失去有关问题根源的信息,请参阅上文。
  • 那么,这是两个邪恶中较小的一个? 有没有更好的办法?

    编辑 :澄清,我不是说积极抛出异常从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)中抛出一个异常,除非在包含进程已被破坏的关键情况下(泄漏,不一致的共享状态等)。

    评论[编辑]:

  • 有准则,而不是硬性规定。 这是一个“AVOID”而不是“DO NOT”指南。 正如所指出的(在评论中)框架在地方打破了这个(和其他)准则。 诀窍是知道何时打破准则。 这在很多方面都是熟练人员和主人之间的区别。
  • 如果清理的某些部分可能会失败,那么应该提供一个Close方法来抛出异常,以便调用者可以处理它们。
  • 如果你遵循处理模式(如果类型直接包含一些非托管资源,那么你应该是这样的),那么可以从终结器调用Dispose(bool)从终结器中抛出是个坏主意,并且会阻止其他对象被终结。
  • 我的观点:从Dispose中逃脱出来的例外应该只是那些像在指南中那样的灾难,因为它足以造成灾难性的后果,使得当前进程中不可能有更多可靠的功能。


    Dispose应该被设计来完成它的目的,处理对象。 这项任务是安全的,大部分时间不会抛出异常 。 如果您发现自己从Dispose抛出异常,您应该考虑两次,看看您是否在做太多的事情。 除此之外,我认为Dispose应该像所有其他方法一样对待:处理是否可以对其进行处理,如果不能处理则让其冒泡。

    编辑:对于指定的例子,我会编写代码,以便我的代码不会导致异常,但清除TcpClient可能会导致异常,这应该是有效的传播在我看来(或作为一个更多的处理和rethrow泛型异常,就像任何方法一样):

    public void Dispose() { 
       if (tcpClient != null)
         tcpClient.Close();
    }
    

    然而,就像任何方法一样,如果你知道tcpClient.Close()可能会抛出一个应该被忽略的异常(无关紧要),或者应该由另一个异常对象来表示,你可能想要捕获它。

    链接地址: http://www.djcxy.com/p/54455.html

    上一篇: 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?