Search code examples
c#disposeidisposable

C# Dispose() standard with certain situation


We know Dispose(bool disposing) should be protected or private, what if i need to manually release the unmanage resources? Dispose() from interface IDISPOSIBLE must call Dispose(true) which means release all resource. i want to manually control the release of manage and unmanage resouces.

The official way to implement Dispose is https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose . however sometime i need to manually release the certain resource by use Dispose(false) should this function be public or do i need create another function like DisposeUnManage() for dispose unmanage resource manually?

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

like this code from TCPStream, i need to use this TCPStream.Dispose(false) method when a TCP client is disconnected. when my TCPServer shutdown i should call 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();
            }
        }

Solution

  • You're clearly abusing the disposable pattern.

    Dispose(bool disposing) is meant to be called exactly once when the object is no longer needed and will never be used again. It can be either because someone directly called Dispose() (which calls Dispose(true)), or because the object's finalizer runs (which calls Dispose(false)). Also, as @Dmytro Mukalov pointed out, using managed objects during Dispose(false) is unsafe and can lead to hard to debug issues (but it's only applicable if your object actually has a finalizer).

    If you're trying to achieve object pooling, I'd suggest you create and then inject a pooling service into this TCPStream. It would manage the lifetime of pooled objects and would allow Rent and Return operations. Then TCPStream.Dispose() will Return any previously Rented objects and get rid of its own resources, while the actual pooled objects will only be disposed when the pooling service gets disposed (which you can set up to be at TCPServer shutdown). As it stands now, your TCPStream has too many responsibilities, which prohibits you from implementing the disposable pattern correctly.