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;
}
}