使用dispose / finalize模式释放套接字/事件/ ummaged代码的正确技术

当我的类包含套接字和事件时,如何实现Dispose模式?

应该是这样的吗?

class MyClass { Socket m_ListenerSocket = new Socket(); book m_Disposed=false; public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool isDisposing) { if (!m_Disposed) { if (isDisposing) { if (m_ListenerSocket != null) { m_ListenerSocket.Dispose(); innerClass.Notify -= Notify; } } //finalized unmanged code here m_Disposed = true; } } ~MyClass() { Dispose(false); } } 

我很困惑…套接字类是“托管代码c#版本的winSock”? 因此,如果用户名为dispose(“isDisposing IS true”),它应该被释放,那么事件处理程序呢?

所以在最终评论部分应该只释放Inptr对象? 谢谢。

我认为有很多方法可以处理一次性物品,无论它们是否有事件。

只是一个疯狂的猜测,但如果你从.net框架中获取一个类并且这个类有一个Dispose()方法,你可以说它是托管代码,所以你只需要调用Dispose方法而不是自己创建析构函数。 这是相当普遍的,因为正如您在下面的示例中所看到的,您还可以在自己的类中实现IDisposable接口,然后以适当的方式处理内部对象。

这对你有帮助吗?

 public class MyClassWithSocket :IDisposable { Socket myInternalSocket = null; public void methodThatUsesSocket() { using (var mySocket = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream , ProtocolType.Tcp)) { //do something with socket //this will be disposed automatically } } public void methodThatUsesInternalSocket() { myInternalSocket = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp); //do other things } public static Socket SomethingThatReturnsSocket() { Socket tempSocket = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp); return tempSocket; } public void Dispose() { myInternalSocket.Dispose(); } } 

由于Socket是一个托管类, 因此实现IDisposable的指导原则指出不需要Finalizer。 实际上,使用终结器实际上会导致对象的垃圾收集被延迟,因为它将在第一次垃圾收集期间调用终结器并且垃圾收集对象第二次运行垃圾收集。

关于事件,您可能希望在Dispose方法中从事件中取消注册,因为事件订阅将导致innerClass保存对MyClass实例的引用,除非innerClass对象是短暂的。 有关事件和Dispose的更多信息,请参阅此问题 。

我认为以下实现对于您的场景是足够的:

 class MyClass : IDisposable { Socket m_listenerSocket = new Socket(); public void Dispose() { m_listenerSocket.Dispose(); innerClass.Notify -= Notify; } } 

您在代码中使用“最终未定义代码”注释的部分应该只释放非托管资源,例如IntPtrforms的句柄等。自从在.NET 2.0中引入SafeHandle以来,您很少需要这样做,因为您可以将IntPtr包装在SafeHandle ,然后将其视为托管资源。

你永远不会让任何事情无动于衷。

根据我的经验,广泛使用的“软错误” Dispose(disposing)机制是错误的构思。 我更喜欢“硬错误”

当我编写实现Dispose()对象时,我通常扩展一个MyDisposable类, #if DEBUG执行以下操作:

  1. 它的构造函数捕获当前堆栈跟踪并将其保存以供日后使用。 (注意:这非常慢,微软只知道原因。)
  2. 该类包含一个初始化为falsebool disposed成员。
  3. Dispose() 声明对象尚未处理,然后将对象标记为已处置。
  4. 对象的析构函数(在完成时调用)检查对象是否已被处置,如果没有,则转储在创建期间记录的堆栈跟踪。 这允许我在我的源代码中找到分配一次性对象但忘记丢弃它的确切位置。

此外,从MyDisposable派生的对象通常断言 !disposed作为每个方法的前提条件,甚至是每个单独的getter。

以上保证:

  1. 只有一种方法可以处理对象。
  2. 每个一次性物体将被布置一次且仅一次。
  3. 一旦处理了对象,就不会调用一次性对象的方法。
  4. 如果我忘记丢弃一次性物品,我会发现,我会确切知道它的分配位置。

不言而喻,如果不是#DEBUG那么MyDisposable几乎不会编译,以免妨碍性能。

因此,我试图避免(在实际中)使用实现“软错误” Dispose(disposing)机制的类,而不首先将它们包装在实现我的“硬错误” Dispose()机制的类中。

现有的答案已经说明了,但它们相当冗长。 让我更清楚地说明这就是你所需要的:

 public void Dispose(){ m_ListenerSocket.Dispose(); } 

这不是删节。 您可以删除其他所有内容。

无需终结者。 无需空测试。 您可以安全地多次处理。

对于可终结的对象(极为罕见)和inheritance(罕见),存在“处置模式”。 几乎总是这种模式根本没有帮助,但会损害代码质量。