0
0
CsharpHow-ToBeginner · 3 min read

How to Use IDisposable in C#: Syntax and Example

In C#, implement the IDisposable interface to release unmanaged resources by defining the Dispose() method. Use using blocks to automatically call Dispose() and clean up resources safely.
📐

Syntax

The IDisposable interface requires implementing a single method Dispose(). This method is where you put code to free resources like files, database connections, or unmanaged memory.

Use the using statement to ensure Dispose() is called automatically when the object is no longer needed.

csharp
public class ResourceHolder : IDisposable
{
    // Flag to detect redundant calls
    private bool disposed = false;

    // Public implementation of Dispose pattern callable by consumers.
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    // Protected implementation of Dispose pattern.
    protected virtual void Dispose(bool disposing)
    {
        if (disposed) return;

        if (disposing)
        {
            // Free managed objects here.
        }

        // Free unmanaged objects here.

        disposed = true;
    }

    ~ResourceHolder()
    {
        Dispose(false);
    }
}
💻

Example

This example shows a class that opens a file and implements IDisposable to close it properly. The using block ensures the file is closed automatically.

csharp
using System;
using System.IO;

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

    public FileManager(string filePath)
    {
        fileStream = new FileStream(filePath, FileMode.OpenOrCreate);
        Console.WriteLine("File opened.");
    }

    public void WriteData(byte[] data)
    {
        if (disposed) throw new ObjectDisposedException("FileManager");
        fileStream.Write(data, 0, data.Length);
        Console.WriteLine("Data written to file.");
    }

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

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

        if (disposing)
        {
            fileStream?.Dispose();
            Console.WriteLine("File closed.");
        }

        disposed = true;
    }

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

class Program
{
    static void Main()
    {
        byte[] data = { 1, 2, 3, 4, 5 };

        using (var manager = new FileManager("testfile.dat"))
        {
            manager.WriteData(data);
        }

        Console.WriteLine("Using block finished.");
    }
}
Output
File opened. Data written to file. File closed. Using block finished.
⚠️

Common Pitfalls

  • Not calling Dispose() leads to resource leaks.
  • Forgetting to suppress finalization with GC.SuppressFinalize(this) can cause unnecessary garbage collection overhead.
  • Accessing disposed objects causes exceptions.
  • Not implementing the full dispose pattern when unmanaged resources are involved can cause bugs.

Always use using blocks or call Dispose() manually to clean up.

csharp
public class BadResource : IDisposable
{
    private bool disposed = false;

    public void Dispose()
    {
        // Forgetting to suppress finalization
        // GC.SuppressFinalize(this); // Missing!
        disposed = true;
    }

    ~BadResource()
    {
        // Finalizer runs even if Dispose was called
    }
}

// Correct way:
public class GoodResource : IDisposable
{
    private bool disposed = false;

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

    protected virtual void Dispose(bool disposing)
    {
        if (disposed) return;
        if (disposing)
        {
            // free managed resources
        }
        // free unmanaged resources
        disposed = true;
    }

    ~GoodResource()
    {
        Dispose(false);
    }
}
📊

Quick Reference

  • Implement IDisposable: Add Dispose() method to clean resources.
  • Use Dispose pattern: Support both manual and finalizer cleanup.
  • Use using block: Automatically calls Dispose() when done.
  • Suppress finalization: Call GC.SuppressFinalize(this) in Dispose().

Key Takeaways

Implement IDisposable and its Dispose() method to release resources.
Use using blocks to ensure Dispose() is called automatically.
Call GC.SuppressFinalize(this) inside Dispose() to optimize garbage collection.
Avoid accessing objects after they have been disposed.
Follow the full dispose pattern when working with unmanaged resources.