Sunday, June 21, 2026

C# Examples of IDisposable Interface to Dispose Unmanaged and Managed Resources

In this post, we will learn about IDisposable interface. This interface contains Dispose() method.
  • IDisposable: It provides a mechanism for releasing unmanaged resources.
  • Dispose(): It performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.

Look at the signature of Dispose method:
public interface IDisposable
{
    void Dispose();
}
The class implementing the IDisposable interface defines Dispose() method as per its need. Look at the first example.
Example1. Dispose No Resource
class DemoFirst : IDisposable
{
    public void Dispose()
    {
        Console.WriteLine("Dispose is not really disposing any resource in DemoFirst.");
    }
}
In the client Program class, an instance of DemoFirst is created and the instance invokes Dispose().
class Program
{
    static void Main(string[] args)
    {
        DemoFirst demo = new DemoFirst();
        demo.Dispose();
    }
}
Now we consider Example2, in which Dispose is implemented to dispose managed resource e.g. FileStream object created in DemoTwo. 

Note. Since FileStream class implements IDisposable, so instance of FileStream can invoke its own Dispose() to dispose the instance.

What are managed resources?
Managed resources are objects managed by the .NET garbage collector. Here are examples:
  • string
  • arrays
  • List<T>
  • FileStream
  • SqlConnection
  • any normal class object
Example
class Testing : IDisposable
{
    private MemoryStream stream = new MemoryStream(); // managed resource
}

Here stream is managed because GC tracks it.

Example2. Dispose Managed resource 
class DemoTwo : IDisposable
{
    private FileStream _fileStream;
    public DemoTwo(string filepath)
    {
        _fileStream = new FileStream(filepath, FileMode.OpenOrCreate);
    }
    public void Dispose()
    {
        Console.WriteLine("Dispose is disposing managed resource Filestream.");
        _fileStream.Dispose(); // Close or Dispose method is used to dispose FileStream object.
    }
}
class Program
{
    static void Main(string[] args)
    {
        var desktop = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
        var file = Path.Combine(desktop, "file1.txt");
        DemoTwo demo = new DemoTwo(file);
        // if use do not call Dispose then
        // file handle resource will not be explicitly disposed.
        // Dispose is used for disposing a resource explicitly
        demo.Dispose();
    }
}
The above examples demonstrates how to dispose a managed resource explicitly by calling Dispose method. The method can be used to dispose unmanaged resource also.

What are unmanaged resources?
Unmanaged resources are things outside GC control. Here are some examples:
  • OS file handles
  • database connection handles
  • socket handles
  • window handles
  • native memory (IntPtr)
  • pointers (int*)
  • COM objects

Now, look at DemoThree class which uses an unmanaged resource. To dispose this resource explicitly, DemoThree class should implement IDisposable interface and the logic to dispose the unmanaged resource be in its body.
Example
class DemoThree : IDisposable
{
    IntPtr nativeBuffer; // unmanaged resource
    // remaining code, logic to dispose etc.
}

Note. GC can free the DemoThree object, but it does not know how to free nativeBuffer. Now see Example3 to know how to free nativeBuffer.

Example3. Dispose Unmanaged Resource 
Look at the Program class which uses DemoThree and object of DemoThree calls Dispose() to release the unmanaged resource explicitly.
using System.Runtime.InteropServices;

class DemoThree : IDisposable
{
    IntPtr nativeBuffer; // unmanaged resource
    public DemoThree()
    {
        nativeBuffer = Marshal.AllocHGlobal(100);
    }
    public void Dispose()
    {
        // release unmanaged memory
        Marshal.FreeHGlobal(nativeBuffer);
    }
}
class Program
{
    static void Main(string[] args)
    {
        DemoThree demo = new DemoThree();
        demo.Dispose(); // call to dispose unmanaged resource
    }
}
Now we see an example to dispose both unmanaged resource and managed resource.

Example4. Dispose Managed Resource & Unmanaged Resource 
using System.Runtime.InteropServices;

class DemoFour : IDisposable
{
    private FileStream file; // managed resource
    private IntPtr nativePtr; // unmanaged resource

    private bool _isDisposed = false; // flag to run if block of GetRidOf method only once

    public DemoFour()
    {
        file = new FileStream("a.txt", FileMode.OpenOrCreate);
        nativePtr = Marshal.AllocHGlobal(100);
    }

    protected virtual void GetRidOf(bool disposing)
    {
        if (!_isDisposed)
        {
            if (disposing)
            {
                // dispose managed objects
                // FileStream has its own Dispose method to dispose its objects
                file?.Dispose();
            }

            // release unmanaged memory
            Marshal.FreeHGlobal(nativePtr);

            _isDisposed = true;
        }
    }

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

    ~DemoFour()
    {
        GetRidOf(false);
    }
}
Code Analysis of Example4
  • Unlike previous examples, here Dispose() method calls a helper method GetRidOf() which can take Boolean value true/false. Note: In Dispose pattern, GetRidOf is usually named as Dispose but I have used non conventional name for clarity.
  • The destructor or finalizer calls GetRidOf(false) with false value. 
  • The Dispose() calls GetRidOf(false) with true value. 
  • Remember that Dispose() is called to explicitly dispose of resources- managed and/or unmanaged.
  • A private Boolean field named _isDisposed is used to call the GetRidOf() only once. Once GetRidOf is called, it cannot be used again to dispose the resources. Reason: When _isDisposed becomes true then inner if block cannot be executed because outer if condition becomes false. 

No comments:

Post a Comment

Hot Topics