IDisposable

Karol Rossa
3 min readJan 19, 2022

We have already covered memory management topics with Garbage Collection and implementing Finalizers in .NET. This time we will talk about how to use IDisposable patterns. Dispose is the best tool for releasing unmanaged resources. Also, whenever you use an object that implements an IDisposable interface, be sure to dispose of it.

There are additional reasons to use Dispose like free allocated memory, removing an item added to a collection, or signaling the release of a lock. Implementation should be idempotent. Subsequent calls shouldn’t cause an exception and basically shouldn’t do anything.

Using Disposable Object

First, let us refresh our memory on how to consume the disposable object. We had to create a ‘using statement’ scope in distant Framework days. It gave us access to an instance within the braces scope. But if we had to use another disposable object within, it led to nasty nesting ;)

Dispose pattern gives us a guarantee that the object will be disposed. Because Under the hood using statement is translated to try-finally block.

Beginning with C# 8.0, we can use the alternative syntax for ‘using statement’ that doesn’t require braces. It comes with all the same benefits and is cleaner. The same code from the first example takes just 3 lines instead of 7.

C# 8.0 also came with IAsyncDisposable. It is used the same way as IDisposable, following the Async pattern.

Implementation

Implementation requires a parameterless method Dispose, but you should also implement its protected version that accepts bool and private bool variable tracking dispose status.

  • public void Dispose()
  • protected virtual void Dispose(bool disposing)
  • private _disposed

Dispose should clean up all objects, so Garbage Collector does not have to call Finalize anymore. Therefore we need to suppress it using GC.SuppressFinalize. Suppressing will enhance performance because GC will not put an instance on the finalization queue.

Protected Dispose accepts the ‘bool disposing’ parameter. It indicates the method’s caller. True for a deterministic call from Dispose. False when receiving a non-deterministic call from the finalizer. You, as an implementer, are responsible for the correct value of the disposing parameter.

The method should do nothing if an instance were already disposed. Then if a call comes from public Dispose, we need to free managed resources. In the end, we will release unmanaged resources regardless of the value of the disposing parameter.

Cascade Dispose

If your class owns a Disposable object as an instance member, it should also implement IDisposable. You are responsible for cleaning your object and all dependencies.

Finalizer and Safe handles

You have to make sure that your object always gets appropriately cleaned up. Unfortunately, you can’t be responsible when someone else consumes your class. It can happen that the coder will forget to dispose of your instance. Fear not, we still have some tools that we can use. Microsoft recommends using Safe Handle. The finalizer is second option but it is more complex to avoid all pitfalls of releasing resources.

SafeHandle is an abstract wrapper for operating system handles. It has some default derived classes for the following handles:

In our example, we will use SafeFileHandle.

Pattern for a class that overrides Finalize is very similar, with the noticeable difference in implementing a finalizer.

Dispose pattern for a derived class

Now I will present just the basic example for a derived class. When you want to derive from the Disposable class, you need to override protected Dispose(bool disposing) and call Dispose on the base class. If your class also uses unmanaged resources, you must remember proper pattern implementation like the two examples we have covered.

--

--