c# - 您是否应该实现 IDisposable.Dispose() 以使其永远不会抛出?

标签 c# .net idisposable

对于 C++ 中的等效机制(析构函数),建议是 it should usually not throw any exceptions .这主要是因为这样做可能会终止您的进程,这很少是一个好策略。

在 .NET 中的等效场景中......

  1. 抛出第一个异常
  2. finally block 作为第一个异常的结果执行
  3. finally block 调用 Dispose() 方法
  4. Dispose() 方法抛出第二个异常

...您的进程不会立即终止。但是,您会丢失信息,因为 .NET 粗暴地将第一个异常替换为第二个异常。因此,调用堆栈上方某处的 catch block 永远不会看到第一个异常。但是,人们通常对第一个异常更感兴趣,因为它通常会提供更好的线索,说明为什么事情开始出错。

由于 .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
        {
        }
    }
}

最佳答案

Framework Design Guidelines(第 2nd 版)将其作为 (§9.4.1):

AVOID throwing an exception from within Dispose(bool) except under critical situations where the containing process has been corrupted (leaks, inconsistent shared state, etc.).

评论[编辑]:

  • 有指导方针,而不是硬性规定。这是一个“避免”而不是“不要”的指南。正如(在评论中)指出的那样,该框架在某些地方打破了这个(和其他)指南。诀窍是知道何时打破准则。在很多方面,这就是熟练工和大师之间的区别。
  • 如果清理的某些部分可能失败,则应提供一个 Close 方法,该方法将抛出异常,以便调用者可以处理它们。
  • 如果您遵循处置模式(如果类型直接包含一些非托管资源,您应该这样做)那么可以从终结器调用 Dispose(bool),从终结器抛出是一个坏主意,会阻止其他对象被最终确定。

我的观点:从 Dispose 中逃逸的异常应该只是那些,如指南中所述,具有足够的灾难性,以至于无法从当前进程中获得进一步的可靠功能。

关于c# - 您是否应该实现 IDisposable.Dispose() 以使其永远不会抛出?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/577607/

相关文章:

c# - 多线程行为奇怪的 C#!

c# - 如何生成与以前在 C# 中随机生成的 key 相同的 key

c# - 如何在 .NET 中显示古埃及象形文字?

c# - 将项目更新到实体核心 5 后出现 Microsoft.EntityFrameworkCore.Query.IParameterValues 错误

c# - 使用相同类型的对象时生成 Cast 异常

c# - 基于相似性比较字符串

.net - <%# %> 和 <%= %> 有什么区别?

.net - WPF 窗口类的 IDisposable 成员

C# 处理 IDisposable

c# - 在链接它们搜索 key 时如何正确关闭RegistryKey?