How to Use Dispose and Finalize in C# with Example?

Do you want to know how to use, dispose, and finalize in C#? In this C# tutorial, I will explain how to use Dispose and Finalize in C# with examples.

In C#, memory management is mostly handled by the Garbage Collector (GC). However, when dealing with unmanaged resources like file handles, network connections, or database connections, these resources are not managed by the GC.

The .NET framework provides the IDisposable interface to allow developers to implement proper cleanup routines for these unmanaged resources. Using the Dispose method, a developer explicitly releases these unmanaged resources without waiting for the garbage collector to do so.

Despite the garbade collector’s efficiency, there are scenarios where it might not reclaim objects for a considerable time after they have stopped being used. If these objects hold onto sizable unmanaged resources, this can lead to memory leaks and resource contention.

To mitigate such issues, the .NET framework offers the Finalize method, which the garbage collector calls on an object if the object’s class provides a finalizer by overriding the Finalize method from its base class Object.

Let us see how to implement them correctly to clean up resources in a C# application.

Dispose and Finalize in C#

In C#, both Dispose and Finalize methods are used for managing and releasing unmanaged resources such as file handles, database connections, and network sockets.

Dispose: A method declared in the IDisposable interface, it is implemented by classes that manage unmanaged resources. When a class implements IDisposable, it should provide an explicit way to release resources on demand.

  • Usage:
    • Invoked manually
    • Employ using statement for automatic invocation

Finalize: This method acts as a safeguard to clean up resources if Dispose is not called. It’s overridden from the Object class.

  • Usage:
    • Automatically executed by the garbage collector
    • Not deterministic (i.e., unpredictable execution time)

Classes containing unmanaged resources should implement IDisposable and provide a Dispose method. This ensures a deterministic way of cleaning resources. The Finalize method adds a layer of protection if Dispose is not called, but relying solely on Finalize is not recommended due to its non-deterministic nature.

The usual pattern involves:

  1. Implementing IDisposable
  2. Providing Dispose method
  3. Optionally overriding Finalize
MethodExecutionDeterminismUsage
DisposeManual/usingDeterministicResource Management
FinalizeGC triggeredNon-deterministicFallback mechanism

The best practice for disposal using these methods depends on ensuring Dispose is called, and resources are released timely with Finalize reserved for cases where Dispose isn’t used.

Implementing IDisposable Interface in C#

Proper implementation of the IDisposable interface helps manage the disposal of resources in a .NET application. It is crucial for releasing unmanaged resources and for providing a mechanism to do so systematically.

Creating the Dispose Method

When implementing the IDisposable interface, the Dispose method is the primary component. The method is responsible for freeing unmanaged resources held by the class. The following is an example of how to implement the Dispose method:

public class ResourceHolder : IDisposable
{
    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)
        {
            if (disposing)
            {
                // TODO: Dispose managed state (managed objects).
            }

            // TODO: Free unmanaged resources (unmanaged objects) and override a finalizer below.
            // TODO: Set large fields to null.

            disposed = true;
        }
    }

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

In this example, the Dispose method performs all object cleanup, freeing resources and suppressing finalization to optimize garbage collection.

Invoking Dispose

Once the Dispose method is in place, it can be called explicitly or implicitly. It’s common practice to use the using statement to ensure Dispose is called automatically:

using (ResourceHolder resource = new ResourceHolder())
{
    // Use resource.
}
// At this point, Dispose method is called implicitly.

This will invoke the Dispose method automatically when the code block is exited, either when the execution is complete or if an exception is thrown, ensuring no resources are unintentionally left allocated.

Using Finalize in C#

In C#, the Finalize method is used for cleanup operations before the garbage collector reclaims an object. This method is overridden to dispose of unmanaged resources.

Defining a Destructor

A destructor is implicitly converted to an override of the Finalize method. It cannot be called directly in the code, as the garbage collector invokes it. To define a destructor for a class in C#:

class SampleClass
{
    // Destructor
    ~SampleClass()
    {
        // Cleanup statements...
    }
}

Destructors are written using the tilde ~ followed by the class name and do not take parameters or have modifiers.

Overriding the Finalize Method

Overriding the Finalize method directly is not common practice because of the introduction of the IDisposable interface. However, if needed, it’s done by the following process. The method is protected and cannot have any access modifiers or parameters.

protected override void Finalize()
{
    try
    {
        // Cleanup statements for unmanaged resources
    }
    finally
    {
        // This ensures that the base class's Finalize method is called 
        base.Finalize();
    }
}

Care should be taken to always call the base.Finalize() method inside a finally block to ensure that base class cleanup is not neglected.

Best Practices for Dispose and Finalize in C#

In managing resources in C#, it is imperative to implement proper patterns to avoid resource leaks and ensure timely resource cleanup. Below are specific practices to follow when working with the Dispose and Finalize methods.

Implementing the Dispose Pattern

The Dispose pattern is used to release unmanaged resources held by an instance of a class. The IDisposable interface contains a Dispose method, which should be implemented to provide a way to release these resources deterministically. Below is an example of the Dispose pattern:

  1. Implement IDisposable and provide the following:
public class ResourceHolder : IDisposable
{
    private bool disposed = false; // To detect redundant calls

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

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // TODO: dispose managed state (managed objects).
            }

            // TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.
            // TODO: set large fields to null.

            disposed = true;
        }
    }

    // Override Finalizer only if Dispose(bool disposing) has code to free unmanaged resources.
    ~ResourceHolder()
    {
        Dispose(false);
    }
}
  1. Invoke Dispose on disposable objects as soon as they are no longer needed, typically using a using statement:
using (var resourceHolder = new ResourceHolder())
{
    // Use resourceHolder
}

The pattern ensures that unmanaged resources are released when the consumer of the class has finished using the object, and it’s also a safeguard to clean up resources in case the consumer forgets to call Dispose.

Handling Unmanaged Resources

When dealing with unmanaged resources, such as file handles, window handles, or database connections managed outside the .NET runtime:

  • Always encapsulate them within a class that implements IDisposable.
  • Ensure that you release these unmanaged resources in the overridden Dispose method.

Example for handling unmanaged resources:

protected override void Dispose(bool disposing)
{
    if (!disposed)
    {
        if (disposing)
        {
            // Dispose managed resources.
        }

        // Free unmanaged resources.
        if (unmanagedResourceHandle != IntPtr.Zero)
        {
            // Call to native method to release unmanaged resource.
            CloseHandle(unmanagedResourceHandle);
            unmanagedResourceHandle = IntPtr.Zero;
        }
        
        disposed = true;
    }
}

It’s crucial to differentiate between freeing managed and unmanaged resources: Managed resources can be cleaned up by the garbage collector, whereas unmanaged resources must be explicitly released by calling the respective cleanup methods provided by the API that handles those resources.

Dispose vs Finalize: When to Use Which

In C#, Dispose and Finalize serve distinct purposes for memory management and resource cleanup. Developers should understand when and how to implement each correctly.

Dispose: Part of the IDisposable interface, Dispose is explicitly called to free unmanaged resources, such as file handles or database connections, which the garbage collector does not collect. It provides a deterministic way to release these resources. Developers should use Dispose in the following situations:

  • When dealing with classes that implement IDisposable.
  • In a using block, ensuring Dispose is called automatically. using (var resource = new Resource()) { // Use resource } // Dispose is called at the end of this block
  • When resources need to be released as soon as they are no longer needed.

Finalize: The Finalize method is called by the garbage collector when an object is no longer accessible. It is overridden to clean up unmanaged resources if Dispose was not called. As it’s non-deterministic and can lead to performance overhead, Finalize should be used sparingly:

  • When an object holding unmanaged resources is not disposed properly.
  • In cases where consumers of a class might forget to call Dispose. ~Resource() { // Cleanup code }

One should not rely on Finalize for timely resource cleanup due to its unpredictability. Instead, implement Dispose and encourage its use. Only implement a finalizer when dealing with unmanaged resources that require explicit cleanup and there is a risk they might not be disposed properly.

Garbage Collection and Finalization

In C#, garbage collection is an automatic memory management feature. It allows for the reclaiming of memory allocated to objects that are no longer in use by the application. Garbage collection is performed by a dedicated Garbage Collector (GC), which runs periodically to evaluate the reachability of objects.

Finalization is a process by which the Garbage Collector offers a last chance to clean up unmanaged resources, like file handles or database connections, before an object is reclaimed. This is achieved through the Finalize method, which can be overridden in a class to include cleanup code.

A class might look like this when implementing finalization:

class ResourceHolder
{
    // Constructor to allocate resources.
    public ResourceHolder() { /* Allocate resources. */ }
    
    // Finalize method for cleanup.
    protected override void Finalize()
    {
        try
        {
            // Cleanup statements.
        }
        finally
        {
            // Ensure that base class Finalize is called.
            base.Finalize();
        }
    }
}

It is important to note that finalization:

  • Delays memory reclaiming since objects with a Finalize method are placed in a separate queue, which needs to be processed twice by the GC.
  • Is non-deterministic, as it is uncertain when the GC will call Finalize.
  • Should not be relied upon for critical resource cleanup; instead, the IDisposable interface and the Dispose method should be used for manual and deterministic release of resources.

Advanced Scenarios

In this section, we explore intricate use cases that showcase the versatility of dispose and finalize in C#. These advanced scenarios include controlling the finalization process, handling disposables in an inheritance hierarchy, and the employment of SafeHandle for unmanaged resources.

Suppressing Finalize

One can prevent a finalizer from running by using the GC.SuppressFinalize() method. This is typically performed in the Dispose method to avoid redundant garbage collection when the object is already cleaned up:

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

Note: It is essential to call GC.SuppressFinalize() only after all necessary finalization has been done to prevent resource leaks.

IDisposable with Inheritance

When dealing with inheritance, the IDisposable pattern needs careful implementation. The base class should declare a Dispose method and the derived class must override it:

public class BaseClass : IDisposable
{
    public void Dispose()
    {
        // Base cleanup
    }
}

public class DerivedClass : BaseClass
{
    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            // Derived cleanup
        }
        base.Dispose(disposing);
    }
}

Important Consideration: Ensure that the derived class’s Dispose method calls the base class’s Dispose to maintain proper resource disposal throughout the hierarchy.

Using SafeHandle

SafeHandle, a wrapper for unmanaged resources, automates the cleanup process, making manual finalization often unnecessary:

public class ResourceHolder : IDisposable
{
    private SafeHandle handle;

    public ResourceHolder()
    {
        // Initialization of SafeHandle
    }

    public void Dispose()
    {
        handle.Dispose();
    }
}

Advantage: Utilizing SafeHandle shifts the responsibility of resource management to the runtime, providing a more reliable garbage collection process.

Example Code: Implementing IDisposable

In this section, the reader will find practical code examples for implementing the IDisposable interface in C#. It demonstrates proper resource management techniques within a class.

Basic Example

public class BasicResourceHolder : IDisposable
{
    // Simulated resource.
    private bool _isDisposed = false; 

    // Implement the IDisposable interface.
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    // Protected implementation of the dispose pattern.
    protected virtual void Dispose(bool disposing)
    {
        if (!_isDisposed)
        {
            if (disposing)
            {
                // Free any other managed objects here.
            }

            // Free any unmanaged objects here.
            _isDisposed = true;
        }
    }

    // Destructor for the BasicResourceHolder.
    ~BasicResourceHolder()
    {
        Dispose(false);
    }
}

In the code above, BasicResourceHolder is a simple class that implements IDisposable. The Dispose() method is called to release resources, and GC.SuppressFinalize(this) is used to prevent the finalizer from running.

Full Dispose Pattern

public class AdvancedResourceHolder : IDisposable
{
    // Flag to indicate if the object has already been disposed.
    private bool _isDisposed = 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 (_isDisposed) return;

        if (disposing)
        {
            // Free any managed objects here.
            // If disposable, dispose them.
            // If an unmanaged resource, set to null.
        }

        // Free any unmanaged resources here.

        _isDisposed = true;
    }

    // Finalizer (destructor).
    ~AdvancedResourceHolder()
    {
        Dispose(false);
    }
}

The second example, AdvancedResourceHolder, illustrates a more thorough implementation of the dispose pattern. This version explicitly cleans up both managed and unmanaged resources, with additional logic in the Dispose(bool disposing) method to distinguish between the two scenarios.

The finalizer ~AdvancedResourceHolder() only cleans up unmanaged resources because managed resources would have already been cleaned up by the garbage collector.

Conclusion

When implementing resource management in C#, developers should consider using Dispose and Finalize to ensure efficient resource utilization. The Dispose method, part of the IDisposable interface, is explicitly called by the developer to release unmanaged resources. On the other hand, the Finalize method is implicitly invoked by the garbage collector and acts as a safety net to clean up resources if Dispose has not been called.

Best practices dictate:

  • Implement IDisposable when dealing with unmanaged resources.
  • Call Dispose() to manually release resources.
  • Use using statements for automatic invocation of Dispose.
  • Override Finalize() with caution, avoiding resource-intensive operations.
  • Invoke GC.SuppressFinalize(this) within Dispose to prevent redundant resource cleanup.

Developers are encouraged to provide a robust disposal pattern, particularly when developing libraries or frameworks, where misuse of resources can lead to significant issues such as memory leaks or performance degradation.

Here is a basic code example:

public class ResourceWrapper : 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.
            }

            // Free unmanaged resources.
            _disposed = true;
        }
    }

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

You may also like: