Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 你觉得我的IDisposable模式实现怎么样?_C#_.net_Design Patterns_Idisposable - Fatal编程技术网

C# 你觉得我的IDisposable模式实现怎么样?

C# 你觉得我的IDisposable模式实现怎么样?,c#,.net,design-patterns,idisposable,C#,.net,Design Patterns,Idisposable,您如何看待以下IDisposable模式实现 public class Connection : IDisposable { private Socket _socket; public bool IsConnected() { if (_socket.Poll(1, SelectMode.SelectRead) && _socket.Available == 0) return false; r

您如何看待以下
IDisposable
模式实现

public class Connection : IDisposable 
{
    private Socket _socket;

    public bool IsConnected()
    {
        if (_socket.Poll(1, SelectMode.SelectRead) && _socket.Available == 0)
            return false;
        return true;
    }

    public void Disconnect()
    {
        if (m_socket != null && IsConnected())
        {
            try
            {
                _socket.Shutdown(SocketShutdown.Both);
                _socket.Disconnect(false);
            }
            catch (SocketException se)
            {
                System.Console.WriteLine(se.Message);
            }
        }
    }

    ~Connection()
    {
        Dispose(false);
    }

    private void Dispose(bool disposing)
    {
        if (!IsConnected())
        {
            if (disposing)
            {
                Disconnect();
            }
            else
            {
                AppDomain currentDomain = AppDomain.CurrentDomain;
                if (currentDomain.IsFinalizingForUnload() && !Environment.HasShutdownStarted)
                {
                     System.Console.WriteLine("Client failed to call Destroy");
                }
            }
        }
    }
}
我使用上面的代码得到了这个错误:

{“尝试对非套接字的对象执行操作”} System.Net.Socket.Socket.Poll(Int32微秒,选择模式)


实施存在严重缺陷。您没有真正实现,最终依赖垃圾收集器来清理资源,这是一件坏事

此外,当GC出现时,您甚至没有正确地清理这些资源(它确实正确地完成了,但这是错误的)

您的类负责实现
IDisposable
,因为您保留了实现
IDisposable
的引用。然后,在您的实现中,如果您没有被GCD(这是对
Dispose
的显式调用),则您需要对您持有的任何
IDisposable
实现调用
Dispose

检查
套接字的连接状态
,但这与对其调用
Dispose
不同,因此导致资源泄漏(GC最终会将其拾取)

有关如何正确实施
IDisposable
的指南,请参阅MSDN文档中标题为“实施Finalize and Dispose以清理非托管资源”的部分,位于以下位置:

我应该注意到,我并不完全同意这些准则,但它们是被采纳最多的。关于我的立场,请参见此处:


首先,您为什么不实施


这将为对象的用户提供一个明确的指示,即他们在完成操作时实际上需要处理它。然后,他们还可以使用块等将其包装在一个
中。

这就是我过去用于一次性图案的方法(我建议从这个开始):

公共类连接:IDisposable
{
#区域配置模式
/// 
///执行与释放、释放或重置非托管资源相关的应用程序定义的任务。
/// 
/// 2
公共空间处置()
{
处置(真实);
总干事(本);
}
私有无效处置(bool处置)
{
ReleaseUnmanagedResources();
如果(处置)
ReleaseManagedResources();
}
/// 
///此类的派生类应重写此方法
///在调用Dispose时清理托管资源。
/// 
受保护的虚拟void ReleaseManagedResources()
{
//在此处输入托管资源清理代码。
}
/// 
///派生类应重写此方法以清除
///调用Dispose时使用非托管资源。
/// 
受保护的虚拟void ReleaseUnmanagedResources()
{
//在此处输入非托管资源清理代码。
}
#端区

我只想将此添加到Richardson指出的模式中

private bool disposed;

private void Dispose(bool disposing)
{
    if(!this.disposed)
    {
        ReleaseUnmanagedResources();
        if (disposing)
            ReleaseManagedResources();
        this.disposed = true;
    }
}

由于以下几个原因,此实现存在缺陷

首先,您的Dispose()方法应该有一个单一的用途——调用
socket.Dispose();
。现在,您在其中加入了太多的逻辑,而不是实际“处理”您拥有的单个受管理、IDisposable资源

第二,您根本不需要终结器,因为您不直接拥有或分配任何本机的非托管资源。您正在处理的唯一资源是一个套接字,它是托管的,并将根据需要实现自己的终结器。如果您想捕获和查找未正确处理连接的情况,我将设置一个仅调试终结器以警告该情况。IDisposable中的终结器用于处理GC必须进行清理的情况,因为调用方忘记调用Dispose()-在您的情况下,套接字的终结器将为您处理该情况

第三,Microsoft设计指南中建议的IDisposable模式的一部分指出,客户端应该能够多次调用Dispose(),而不会产生任何后果。在第一次调用Dispose()之后,不应该直接使用套接字-事实上,我建议Dispose()应该调用
socket.Close();
(socket作为IDisposable)。Dispose();
并立即设置
socket=null;
以防止出现这种情况。根据当前逻辑,很有可能在
IsConnected()
中调用导致套接字在后续调用Dispose()时引发异常,这应该避免

第四,强烈建议对使用文件、套接字或其他“可关闭”资源的所有资源使用Close()方法。Close()应调用Dispose()


最后,应该在处理后检查连接的使用情况。处理后使用的连接上的任何方法都应该抛出ObjectDisposedException。

其他答案中似乎没有明确说明这一点,因此,如果您对出现的具体问题感兴趣,请看这里

当两个对象有终结器并且被GC回收时,它们的终结器的执行没有特定的顺序。在您的代码中,Socket类有一个终结器,而您的类有一个终结器。如果终结器首先在Socket实例上执行,那么当终结器执行时,您将尝试调用方法on已最终确定的对象,因此为异常

终结器基本上很糟糕。您几乎不需要编写终结器(即使您处理的是原始Win32句柄-请改用
SafeHandle


相反,只需实现IDisposable,不要编写终结器。

终结器很麻烦,如果可能,您应该避免调用它们。但是,如果实现IDisposable,您几乎总是希望编写一个终结器(与Dispose(bool)方法结合使用)。这会调用Dispose(false)和
private bool disposed;

private void Dispose(bool disposing)
{
    if(!this.disposed)
    {
        ReleaseUnmanagedResources();
        if (disposing)
            ReleaseManagedResources();
        this.disposed = true;
    }
}