【问题标题】:C# Dispose() standard with certain situation特定情况下的 C# Dispose() 标准
【发布时间】:2020-02-03 19:15:50
【问题描述】:

我们知道 Dispose(bool disposing) 应该是受保护的或私有的,如果我需要手动释放未管理的资源怎么办?来自接口 IDISPOSIBLE 的 Dispose() 必须调用 Dispose(true) ,这意味着释放所有资源。我想手动控制管理和取消管理资源的释放。

实现 Dispose 的官方方式是 https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose 。 但是有时我需要使用 Dispose(false) 手动释放某些资源,这个函数应该是公共的,还是我需要创建另一个函数,如 DisposeUnManage() 来手动释放取消管理资源?

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

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

      if (disposing) {
         handle.Dispose();
         // Free any other managed objects here.

      }

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

public void DisposeUnmanage()
{
Dispose(false);
}

private void Dispose(bool disposing)
   {
      if (disposed)
         return; 

      if (disposing) {
         handle.Dispose();
         // Free any other managed objects here.

      }

      disposed = true;
   }

像 TCPStream 中的这段代码一样,当 TCP 客户端断开连接时,我需要使用这个 TCPStream.Dispose(false) 方法。当我的 TCPServer 关闭时,我应该调用 TCPStream.Dispose(true)。

/// <summary>
        /// Closes the underlying socket
        /// </summary>
        /// <param name="disposing">
        /// If true, the EventArg objects will be disposed instead of being re-added to 
        /// the IO pool. This should NEVER be set to true unless we are shutting down the server!
        /// </param>
        private void Dispose(bool disposeEventArgs = false)
        {
            // Set that the socket is being closed once, and properly
            if (SocketClosed) return;
            SocketClosed = true;

            // If we need to dispose out EventArgs
            if (disposeEventArgs)
            {
                ReadEventArgs.Dispose();
                WriteEventArgs.Dispose();
                DisposedEventArgs = true;
            }
            else
            {
                // Finally, release this stream so we can allow a new connection
                SocketManager.Release(this);
                Released = true;
            }

            // Do a shutdown before you close the socket
            try
            {
                Connection.Shutdown(SocketShutdown.Both);
            }
            catch (Exception) { }
            finally
            {
                // Unregister for vents
                ReadEventArgs.Completed -= IOComplete;
                WriteEventArgs.Completed -= IOComplete;

                // Close the connection
                Connection.Close();
                Connection = null;
            }           

            // Call Disconnect Event
            if (!DisconnectEventCalled && OnDisconnected != null)
            {
                DisconnectEventCalled = true;
                OnDisconnected();
            }
        }

【问题讨论】:

  • 仅为非托管资源创建或使用IDisposable 类型,并将其传递给构造函数(依赖注入器)。然后调用者可以处理它并......等等,如果调用者控制非托管资源的处理,那不会让你的类型处于不稳定状态吗?我闻到了XY problem,你真正需要解决的是什么?
  • "有时我需要使用 Dispose(false) 手动释放某些资源" -- 这是什么意思?将false 传递给Dispose(bool) 用于终结器,即释放 非托管资源,因为运行时将负责处理可终结对象。你不应该在其他时间调用它。我怀疑是否需要能够处理您拥有的对象的某些子集,但如果必须,只需为客户端代码调用一个完全独立的公共方法。不过也许你应该写一个缓存。
  • 一次性类背后的想法是它包装/隐藏调用者的资源。调用者只需要在完成后调用 Dispose。它不应该像这样直接管理它。
  • 如果您需要释放某些资源并留下其他资源,则可能表明存在严重的设计缺陷,尤其是职责分解问题。顺便说一句,您的 Dispose 方法非常不安全 - 无论如何调用它,它都使用托管对象,并且从终结器触发可能会导致严重问题。

标签: c# dispose idisposable


【解决方案1】:

你显然在滥用一次性模式。

Dispose(bool disposing) 意味着在不再需要对象时只调用一次并且永远不会再次使用。可能是因为有人直接调用了Dispose()(调用Dispose(true)),也可能是因为对象的终结器运行(调用Dispose(false))。此外,正如@Dmytro Mukalov 所指出的,在Dispose(false) 期间使用托管对象是不安全的,并且可能导致难以调试的问题(但它仅适用于您的对象实际上具有终结器的情况)。

如果你想实现对象池,我建议你创建一个池服务,然后将池服务注入这个TCPStream。它将管理池对象的生命周期,并允许RentReturn 操作。然后TCPStream.Dispose()Return 任何以前的Rented 对象并摆脱它自己的资源,而实际的池化对象只会在池化服务被释放时才会被释放(您可以设置为TCPServer关掉)。就目前而言,您的TCPStream 的职责太多,这使您无法正确实现一次性模式。

【讨论】:

  • 非常感谢,您能帮我重构一下 TCPStream 类吗?怎么联系你?
  • @xiaojiuwo 如果您需要帮助重构,您应该将您的代码发布到Code Review SE。你会接触到一个比我更聪明的人,可以帮助你。
  • thx,顺便说一句,我找到了另一个有用的代码,所以我不需要重构它。再次感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-22
  • 2014-06-16
  • 1970-01-01
  • 1970-01-01
  • 2018-10-29
相关资源
最近更新 更多