在 API 中实现 IDisposable

Implementing IDisposable in an API

我目前正在编写一个相当大的 API 并且对于我应该如何实现 IDisposable 感到困惑。在简化的场景中:

我的一个 classes 中有一个套接字(称之为 A),显然需要处理,但是 class 只有内部构造函数,因此我的用户只会能够通过创建更高级别对象(称为 B)的实例来创建它的实例,该实例将依次实例化 A 并因此获得新对象 A 的所有权。

我的直觉建议让对象 A 和 B 都成为 IDisposable,这样当用户在他们的应用程序中释放我们拥有的对象 B 时,A 也会被释放,套接字也会被正确释放。但是,由于这是一个 API,它允许我的用户处理对象 A 和套接字而不处理拥有对象 B,因此可能会导致一些重大问题。

那么我该如何进行呢?据我所知,我唯一的选择是糟糕的选择:

  1. 按照直觉实现 IDisposable 并告诉我的用户不要在文档中犯傻。 (非常糟糕)
  2. 按照直觉实现 IDisposable,但在整个应用程序中添加一些可怕的处置 checking/exceptions 以防万一(尽管在我的应用程序中处置整个小节对应用程序来说无论如何都是致命的)。
  3. 只在对象 B 中实现 IDisposable,而是添加一个内部 Dispose 方法来安全地处理 A 和套接字。 (貌似最好的主意)

我不确定我是否还有其他选择,所以我真的希望有人能给我建议。

谢谢。

好吧,如果你不希望 api 用户处理你的套接字,而不是仅仅隐藏它(制作一些包装器或套接字私有的东西)。

否则处置通常是这样实现的,因此您可以执行级联处置并且您真的不介意是否已经处置了一些东西。

public class ComplexResourceHolder : IDisposable {

private IntPtr buffer; // unmanaged memory buffer
private SafeHandle resource; // disposable handle to a resource

public ComplexResourceHolder(){
    this.buffer = ... // allocates memory
    this.resource = ... // allocates the resource
}

protected virtual void Dispose(bool disposing){
        ReleaseBuffer(buffer); // release unmanaged memory
    if (disposing){ // release other disposable objects
        if (resource!= null) resource.Dispose();
    }
}

~ ComplexResourceHolder(){
    Dispose(false);
}

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

}

代码示例取自 msdn,您可以在其中找到更详细的解释。

My intuition suggests to make both objects A and B IDisposable so that when the user disposes of our owning object B in their application, A is disposed of and the socket gets correctly disposed of as well. However, since this is an API, it would allow my user to dispose object A and the socket without disposing of the owning object B and hence could cause themselves some major issues.

同时实现 IDisposable 可能是最好的主意,它清楚地说明了需要清理代码实例的事实。指示 API 的使用者调用 root/owning 对象上的 Dispose (B)。要解决 API 的消费者不小心也在拥有的 (A) 实例上调用 Dispose 的问题,您可以执行以下操作。

隐藏它

不要从 root/owning 对象 B 公开拥有的引用 A 或提供有限数量的传递方法调用根并传递到拥有的实例然后再次返回。如果您谈论的是真正有限的 methods/properties 不会改变,这很好。

接口

使用包装器和接口包装套接字访问(我在这里假设套接字是 .net socket class 但是如果它是你自己围绕套接字实现的对象 class 那么你的 class 已经是包装器了,不需要围绕它创建另一个包装器 )。包装器应实现 IDisposable 并且接口应仅公开您希望客户端访问的内容。我不确定您在哪里创建套接字实例,但它们应该由拥有的对象或使用工厂模式创建,以便在创建后尽快建立所有者关系。您可以将 class 定义保留在内部,只向 API.

公开一个接口

在我看来,这是最好的方法,如果您需要扩展 API。

,可以在将来灵活更改
public class Owner : IDisposable
{
    private SocketWrapper _wrapper;
    public ISocketWrapper SocketAccess { get { return _wrapper; } }
    public void Dispose()
    {
        if (_wrapper != null)
            _wrapper.Dispose();
    }
}

public interface ISocketWrapper
{
    // exposed properties/methods
}


internal class SocketWrapper : ISocketWrapper, IDisposable
{
    public void Dispose()
    {
        // dispose socket
    }
}