【问题标题】:C# Dispose resources on exceptions without usingC# 在不使用的情况下处理异常资源
【发布时间】:2020-03-08 02:57:49
【问题描述】:

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

通常我会使用“using”语句,但这在本地范围之外不起作用。就我而言,我正在上课并且在许多公共方法中使用 Socket。连接结束时很容易处理 Socket,但是在没有“使用”块的情况下抛出异常时如何处理呢?

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

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

public class Foo
{
    Socket _socket;

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

【问题讨论】:

  • 是的,您应该依靠您的消费者来处理它。您可以通过让您的类自己实现 IDisposable 来向您的消费者发出信号。如果他没有处理你的对象,这会给你的消费者一个警告,假设他正在使用 FxCop 或类似的工具

标签: c# dispose idisposable using-statement


【解决方案1】:

您有 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
  }
}

【讨论】:

    【解决方案2】:

    从错误开始

    例如,您应该在构造函数中创建套接字并在析构函数中将其释放,否则在方法 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 方法中释放套接字。

    它将更加干净和健壮。

    所以你可以使用 Foo 和 using 模式,或者你可以调用 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

    【讨论】:

    • @OlivierRogier 我强烈建议您删除第一个代码块。终结器绝对在这里是错误的工具(从这样的终结器中Dispose 的字段是不安全的,而且它是不必要的,因为Socket 的终结器会处理它无论如何同时)。第二个代码块很好(尽管如果它使用像 Dmitry's 这样的标准模式会很好)。我从来没有写过终结器。 devblogs.microsoft.com/cbrumme/finalizationjoeduffyblog.com/2005/04/08/… 可能值得一读。
    • 对不起,我不清楚。你真的应该删除~Foo() { _socket.Dispose(); } 这是不安全的。 cbrumme 的文章讨论了为什么它是不安全的。
    猜你喜欢
    • 2012-05-05
    • 2011-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-19
    • 1970-01-01
    • 2015-07-23
    相关资源
    最近更新 更多