Sunday, June 21, 2026

C# Understanding The Dispose Pattern

This is second part of IDisposable post. If you have basic understanding of IDisposable then continue this post,else read: C# IDisposable Interface to Dispose Unmanaged and Managed Resources.

Look at the following code and answer this question: why is exception not thrown when Dispose is called again by the same demo object? Your guess should be that first call of demo.Dispose(); disposes off FileStream object then calling again demo.Dispose(); should throw error. But on running the code, you get no error. This is due to Dispose Pattern implemented inside FileStream class. Remember that FileSream implements IDisposable.

class DemoTwo : IDisposable
{
    private FileStream _fileStream;
    public DemoTwo(string filepath)
    {
        _fileStream = new FileStream(filepath, FileMode.OpenOrCreate);
    }
    public void CanReadFile()
    {
        Console.WriteLine($"Can read the file: {_fileStream.CanRead}");
    }
    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.CanReadFile();
        demo.Dispose(); // first call
        demo.Dispose(); // second call
        demo.CanReadFile();
    }
}
/*
Can read the file: True
Dispose is disposing managed resource Filestream.
Dispose is disposing managed resource Filestream.
Can read the file: False   
*/
Answer: If a resource is already gone, you would naturally expect the system to complain if you try to destroy it again. However, the reason it doesn't throw an exception comes down to the official design guidelines for the .NET Framework, specifically regarding the IDisposable interface. 

1. The IDisposable Contract: Idempotency 
According to the official Microsoft documentation, the Dispose method must be idempotent. This is a fancy way of saying that calling Dispose multiple times must be safe and should not throw an exception. 

From Microsoft's Guidelines: > "If an object's Dispose method is called more than once, the object must ignore all calls after the first one. The method must not throw an exception if called multiple times." 

Because FileStream is a built-in .NET class, its own Dispose method strictly follows this rule. When your code calls _fileStream.Dispose() the second time, the FileStream internally checks if it has already been disposed, sees that it has, and quietly does nothing. 

2. How to Write it Properly (The Dispose Pattern)
While the code currently relies on the inner FileStream to safely handle the duplicate call, DemoTwo class itself isn't tracking its own disposal state. If you had other logic in Dispose method, it would run multiple times, which you usually want to avoid. The standard way to implement this is by using a _disposed flag to keep track of the object's state. Here is how you can update your class to follow the recommended Dispose Pattern:
class DemoTwo : IDisposable
{
    private FileStream _fileStream;
    private bool _disposed = false; // Track whether Dispose has been called.
    public DemoTwo(string filepath)
    {
        _fileStream = new FileStream(filepath, FileMode.OpenOrCreate);
    }
    public void Dispose()
    {
        // 1. Check if it's already been disposed
        if (_disposed)
        {
            Console.WriteLine("Dispose already called. Ignoring.");
            return;
        }
        // 2. Clean up resources
        Console.WriteLine("Dispose is disposing managed resource Filestream.");
        _fileStream?.Dispose();
        // 3. Mark as disposed so subsequent calls do nothing
        _disposed = true;
    }
}
Summary 
  • Why no error? Because FileStream.Dispose() is built to safely ignore repeated calls without throwing errors. 
  • When do exceptions happen? If you try to use the _fileStream (like trying to read or write to it) after it has been disposed, .NET will immediately throw an ObjectDisposedException. But calling Dispose itself is always a safe zone.
  • Calling I/O operation methods e.g. Read, Write, ReadByte etc. after disposing FileStream object, will throw ObjectDisposedException object. 
  • But the CanReadFile() calls state tracking property on FileStream object will not throw exception. The Dispose Pattern is designed to safely handle state information even if FileStream object is disposed.

No comments:

Post a Comment

Hot Topics