.net 在API中实现IDisposable

zbdgwd5y  于 2023-01-27  发布在  .NET
关注(0)|答案(3)|浏览(141)

我目前正在编写一个相当大的API,对于如何实现IDisposable感到困惑。在一个简化的场景中:
我在我的一个类中有一个Socket(称之为A),它显然需要处理,但是该类只有内部构造函数,因此我的用户只能通过创建一个更高级别对象(称之为B)的示例来创建它的示例,该对象反过来示例化A,从而获得新对象A的所有权。
我的直觉建议将对象A和B都设置为IDisposable,这样当用户在应用程序中释放我们的所有对象B时,A也会被释放,套接字也会被正确地释放。然而,由于这是一个API,它将允许我的用户在不释放所有对象B的情况下释放对象A和套接字,因此可能会给他们自己带来一些主要问题。
那我该怎么做呢?在我看来我只有坏的选择:
1.按照直觉实现IDisposable,并在文档中告诉我的用户不要愚蠢。(非常糟糕)
1.按照直觉实现IDisposable,但在整个应用程序中添加一些可怕的处置检查/异常以防万一(尽管在我的应用程序中,处置整个子部分对应用程序来说无论如何都是致命的)。
1.只在对象B中实现IDisposable,并添加一个内部Dispose方法来安全地处理A和套接字(似乎是最好的主意)。
我真的不知道我是否有任何其他的选择,所以我真的希望有人能给我建议。
谢谢。

flvtvl50

flvtvl501#

我的直觉建议将对象A和B都设置为IDisposable,这样当用户在应用程序中释放我们的所有对象B时,A也会被释放,套接字也会被正确地释放。然而,由于这是一个API,它将允许我的用户在不释放所有对象B的情况下释放对象A和套接字,因此可能会给他们自己带来一些主要问题。
使两者都实现IDisposable可能是最好的主意,并且清楚地说明了代码示例需要清理的事实。指示API的使用者在根/拥有对象(B)上调用Dispose。要解决API的使用者意外地也在拥有的(A)示例上调用Dispose的问题,您可以执行以下操作。
"藏起来"
不要从根/拥有对象B公开拥有引用A,或者提供有限数量的传递方法,这些方法调用根并传递到拥有示例,然后再返回。如果你谈论的是真正有限数量的不会改变的方法/属性,这是很好的。

    • 接口**

使用 Package 器和接口 Package 套接字访问(* 我在这里假设套接字是一个. net socket class,但是如果它是你自己的套接字类周围的对象实现,那么你的类已经是 Package 器了,不需要再创建另一个 Package 器了 *) Package 器应该实现IDisposable,接口应该只公开你希望客户端访问的内容。我不确定你在哪里创建套接字的示例,但是它们应该由被拥有的对象或者使用工厂模式来创建,这样所有者关系在创建后就能尽快建立。你可以保持类定义在内部,并且只向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
    }
}
44u64gxh

44u64gxh2#

如果你不想让API用户处理你的socket,那就把它隐藏起来(做一些socket是私有的 Package 器或其他东西)。
否则,dispose通常是这样实现的,这样您就可以执行级联dispose,而不会真正介意是否已经释放了某些内容。

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,您可以在其中找到更详细的说明。

kh212irz

kh212irz3#

在实现IDisposable时要记住的一件重要的事情是不要处理不属于您的IDisposable示例(那些不是您的类专门创建的示例)。
因此,在您的情况下,选项3将是可取的。

相关问题