C# 正确使用带有 SafeSocketHandle 的 Dispose

C# proper use of Dispose with SafeSocketHandle

我知道 dispose pattern 并想正确处理我的 Socket 资源。 在文档中他们建议使用 SafeHandles, but I'm not sure how exactly this works with System.Net.Socket. I discovered that the Socket class itself includes a SafeSocketHandle,但我不知道如何使用它。我需要处理手柄和插座还是只处理手柄就足够了?我假设在 class 中我只使用句柄进行套接字操作,对吗?

public class MySocket : IDisposable
{
    private SafeSocketHandle _handle;
    private bool _disposed = false;

    public MySocket(AddressFamily addressFamily)
    {
        Socket s = new Socket(addressFamily, SocketType.Stream, ProtocolType.Tcp);
        _handle = s.SafeHandle;
    }

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

    protected virtual void Dispose(bool disposing)
    {
        if (_disposed)
            return;

        if (disposing)
        {
            _handle.Dispose();
        }

        _disposed = true;
    }
}

也许有人也能解释为什么要使用 SafeHandles? 我的意思是这还不够吗?:

public class MySocket : IDisposable
{
    private Socket _socket;
    private bool _disposed = false;

    public MySocket(AddressFamily addressFamily)
    {
        _socket = new Socket(addressFamily, SocketType.Stream, ProtocolType.Tcp);
    }

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

    protected virtual void Dispose(bool disposing)
    {
        if (_disposed)
            return;

        if (disposing)
        {
            _socket.Dispose();
        }

        _disposed = true;
    }
}

当您直接管理非托管资源时,您应该使用 SafeHandle

对于 Socket,有一个底层非托管资源,但它由 Socket class 处理。您无需参与其中——Socket 本身是托管资源,而不是非托管资源。 Socket 有自己的终结器,如果您忘记处置它,它将(可能)释放底层的非托管资源。

所以,在这里你不用担心SafeHandles。只需实施 IDisposable 并调用 _socket.Dispose().


不需要在此处实现完整的 Dispose 模式,除非您可能有一个派生的 class,它拥有自己的非托管资源。写就够了:

public class MySocket : IDisposable
{
    private Socket _socket;
    private bool _disposed = false;

    public MySocket(AddressFamily addressFamily)
    {
        _socket = new Socket(addressFamily, SocketType.Stream, ProtocolType.Tcp);
    }

    public void Dispose()
    {
        _socket.Dispose();
        _disposed = true;
    }
}

事实上,许多编码标准要求非托管资源 必须 包装在 SafeHandle 中。如果这样做,则无需实施完整的 Dispose 模式。

如果您需要实现完整的处理模式,您还需要编写终结器。您问题中的代码不会执行此操作。

事实上,dispose 模式鼓励用户走上一条相当危险的道路——在 void Dispose(bool disposing) 方法中编写正确的代码确实非常困难,并且存在大多数用户都没有意识到的陷阱。这就是在 .NET 2.0 中引入 SafeHandle 的原因——运行时对其有特殊支持,从而避免了这些陷阱——但关于 IDisposable 的建议似乎从未更新过。