C# 在不使用的情况下处理异常资源

C# Dispose resources on exceptions without using

我正在编写一个包含套接字操作的 class 库。我不想依赖消费者在完成后或抛出异常时处理资源。

通常我会使用 "using" 语句,但这在本地范围之外不起作用。在我的例子中,我有一个 class 并在许多 public 方法中使用套接字。连接结束时处理 Socket 很容易,但是在没有 "using" 块的情况下抛出异常时如何处理它?

这是我想出的: 基本上,我用 try catch 块封装了用户可以访问的每个方法。如果发生异常,我会在向用户抛出异常之前处理资源。

但我想知道是否有更好的选择。如果我对图书馆消费者隐藏这个自动处理是不好的。

public class Foo
{
    Socket _socket;

    public void method1()
    {
         try
         {
              // some work with _socket
         }
         catch
         {
             _socket.Dispose();
             throw;
         }
    }
}

从错的地方说起

例如,您应该在构造函数中创建套接字并在析构函数中处理它,否则在像方法 1 中那样捕获到第一个异常之后,您将拥有一个可以继续使用的已处理对象的引用...

public class Foo
{
  private Socket _socket { get; }

  public Foo()
  {
    _socket = new Socket();
  }

  ~Foo()
  {
    _socket.Dispose();
  }

  public void method1()
  {
    try
    {
      // some work with _socket
    }
    catch
    {
      throw;
    }
  }
}

做正确的事

理想情况下,您应该将 Foo 标记为实现 IDispose 并在 Dispose 方法中释放套接字。

它将更加干净和健壮。

所以你可以通过 using 模式使用 Foo,或者你可以调用 Foo.Dispose.

public class Foo : IDisposable
{
  private Socket _socket { get; }

  private bool IsDisposed;

  public Foo()
  {
    _socket = new Socket();
  }

  public void Dispose()
  {
    Dispose(true);
    GC.SuppressFinalize(this);
  }

  protected virtual void Dispose(bool disposing)
  {
    if ( IsDisposed ) return;
    if ( disposing )
    {
      if ( _socket != null )
        try
        {
          _socket.Dispose();
          _socket = null;
        }
        catch ( Exception ex )
        {
          // Show message or do nothing
        }
    }
    IsDisposed = true;
  }
}

Implementing a Dispose method

您有 2 个备选方案:

使用标准 IDisposable 实现

  public class Foo : IDisposable {
    Socket _socket;

    ...

    public void method1() {
      // working with _socket
    }

    protected virtual void Dispose(bool disposing) {
      if (disposing) {
        if (_socket != null) {
          _socket.Dispose();

          _socket = null;
        }  
      }
    }  

    public void Dispose() {
      Dispose(true); 
    }
  }

或者把_socket变成局部变量:

public void method1() {
  using (Socket _socket = new Socket(...)) {
    // working with _socket
  }
}