How to implement IDisposable Design Pattern in C#?

The IDisposable design pattern (also called the Dispose Pattern) in C# is used to properly clean up unmanaged resources like file handles, database connections, and network streams. This pattern ensures that resources are released deterministically, rather than waiting for the garbage collector.

Classes that directly or indirectly use unmanaged resources should implement the IDisposable interface. This includes classes that use FileStream, HttpClient, database connections, or any other objects that hold system resources.

Syntax

Following is the basic syntax for implementing IDisposable −

public class ClassName : IDisposable {
   private bool disposed = false;
   
   public void Dispose() {
      Dispose(true);
      GC.SuppressFinalize(this);
   }
   
   protected virtual void Dispose(bool disposing) {
      if (!disposed) {
         if (disposing) {
            // Dispose managed resources
         }
         // Clean up unmanaged resources
         disposed = true;
      }
   }
   
   ~ClassName() {
      Dispose(false);
   }
}

Key Components of the Pattern

IDisposable Pattern Components Public Dispose() Entry point for cleanup Protected Dispose(bool) Core cleanup logic Overridable Finalizer ~Class() Backup cleanup by GC Resource Cleanup Managed and Unmanaged Resources disposing = true Clean managed resources disposing = false Clean unmanaged only

Using IDisposable with File Operations

Example

using System;
using System.IO;

public class FileManager : IDisposable {
   private FileStream fileStream;
   private bool disposed = false;

   public FileManager(string fileName) {
      fileStream = new FileStream(fileName, FileMode.Create);
   }

   public void WriteData(string data) {
      if (disposed) {
         throw new ObjectDisposedException("FileManager");
      }
      byte[] bytes = System.Text.Encoding.UTF8.GetBytes(data);
      fileStream.Write(bytes, 0, bytes.Length);
   }

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

   protected virtual void Dispose(bool disposing) {
      if (!disposed) {
         if (disposing) {
            // Dispose managed resources
            fileStream?.Dispose();
         }
         // Clean up unmanaged resources here if any
         disposed = true;
      }
   }

   ~FileManager() {
      Dispose(false);
   }
}

class Program {
   static void Main(string[] args) {
      using (var fileManager = new FileManager("test.txt")) {
         fileManager.WriteData("Hello, World!");
         fileManager.WriteData("IDisposable Pattern Example");
      } // Dispose() called automatically
      Console.WriteLine("File operations completed and resources disposed.");
   }
}

The output of the above code is −

File operations completed and resources disposed.

Using 'using' Statement for Automatic Disposal

Example

using System;

public class DatabaseConnection : IDisposable {
   private string connectionString;
   private bool disposed = false;

   public DatabaseConnection(string connString) {
      connectionString = connString;
      Console.WriteLine("Database connection opened");
   }

   public void ExecuteQuery(string query) {
      if (disposed) {
         throw new ObjectDisposedException("DatabaseConnection");
      }
      Console.WriteLine($"Executing query: {query}");
   }

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

   protected virtual void Dispose(bool disposing) {
      if (!disposed) {
         if (disposing) {
            Console.WriteLine("Database connection closed");
         }
         disposed = true;
      }
   }

   ~DatabaseConnection() {
      Dispose(false);
   }
}

class Program {
   static void Main(string[] args) {
      // Using statement ensures automatic disposal
      using var connection = new DatabaseConnection("server=localhost;database=test");
      connection.ExecuteQuery("SELECT * FROM users");
      connection.ExecuteQuery("UPDATE users SET active=1");
      
      Console.WriteLine("Operations completed");
   } // Dispose() called automatically here
}

The output of the above code is −

Database connection opened
Executing query: SELECT * FROM users
Executing query: UPDATE users SET active=1
Operations completed
Database connection closed

IDisposable with Inheritance

Example

using System;

public class BaseResource : IDisposable {
   private bool disposed = false;

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

   protected virtual void Dispose(bool disposing) {
      if (!disposed) {
         if (disposing) {
            Console.WriteLine("BaseResource: Disposing managed resources");
         }
         Console.WriteLine("BaseResource: Disposing unmanaged resources");
         disposed = true;
      }
   }

   ~BaseResource() {
      Dispose(false);
   }
}

public class DerivedResource : BaseResource {
   private bool disposed = false;

   protected override void Dispose(bool disposing) {
      if (!disposed) {
         if (disposing) {
            Console.WriteLine("DerivedResource: Disposing managed resources");
         }
         Console.WriteLine("DerivedResource: Disposing unmanaged resources");
         disposed = true;
      }
      // Call base class Dispose method
      base.Dispose(disposing);
   }
}

class Program {
   static void Main(string[] args) {
      using var resource = new DerivedResource();
      Console.WriteLine("Resource created and used");
   } // Dispose chain called automatically
}

The output of the above code is −

Resource created and used
DerivedResource: Disposing managed resources
DerivedResource: Disposing unmanaged resources
BaseResource: Disposing managed resources
BaseResource: Disposing unmanaged resources

Best Practices

  • Always call GC.SuppressFinalize(this) in the public Dispose() method to prevent the finalizer from running.

  • Make the protected Dispose(bool disposing) method virtual so derived classes can override it.

  • Check the disposed flag to prevent multiple dispose calls.

  • Use the using statement or using declaration for automatic disposal.

  • Throw ObjectDisposedException when methods are called after disposal.

Conclusion

The IDisposable design pattern in C# provides a standardized way to release unmanaged resources deterministically. It consists of a public Dispose() method, a protected virtual Dispose(bool disposing) method, and optionally a finalizer for backup cleanup. Use the using statement to ensure automatic disposal and prevent resource leaks.

Updated on: 2026-03-17T07:04:36+05:30

2K+ Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements