8

当我的类包含套接字和事件时如何实现 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 对象?谢谢。

4

4 回答 4

3

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

只是一个疯狂的猜测,但是如果你从 .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();
    }
}
于 2012-11-13T07:43:15.660 回答
2

由于 Socket 是一个托管类,因此实现指南IDisposable表明不需要终结器。事实上,拥有终结器实际上会导致对象的垃圾收集延迟,因为它会在第一次垃圾收集期间调用终结器,而在第二次运行垃圾收集时对对象进行垃圾收集。

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

我认为以下实现足以满足您的场景:

class MyClass : IDisposable
{
   Socket m_listenerSocket = new Socket();

   public void Dispose()
   {
       m_listenerSocket.Dispose();
       innerClass.Notify -= Notify; 
   }
}

你的代码中你用“finalized unmanged code here”注释的部分应该只释放非托管资源,例如形式的句柄IntPtr等。自从SafeHandle在.NET 2.0中引入以来,你很少需要这样做,因为你可以将您包装IntPtr在 a 中SafeHandle,然后将其视为托管资源。

于 2012-11-13T10:28:27.167 回答
1

你绝不能让任何事情不被肯定。

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

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

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

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

以上保证:

  1. 只有一种方式可以处理对象。
  2. 每个一次性物品将被处理一次且仅一次。
  3. 一次性对象的任何方法都不会在对象被释放后被调用。
  4. 如果我忘记处理一次性对象,我会找出来,并且我会确切知道它被分配到哪里。

不言而喻,如果不是,#DEBUGMyDisposable编译几乎没有,以免影响性能。

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

于 2015-02-17T09:52:42.737 回答
0

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

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

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

不需要终结器。不需要空测试。您可以安全地多次处理。

“处置模式”存在于可终结的对象(非常罕见)和继承(罕见)。这种模式几乎总是无济于事,反而会损害代码质量。

于 2015-02-17T10:16:52.833 回答