Skip to content

How to Resolve Error "CS0249: Do not override object.Finalize" in C#

Learn how to fix the C# compiler error CS0249 by using destructors (~ClassName) instead of overriding object.Finalize, and understand the proper finalization pattern in .NET.

The Compiler Error CS0249 is a syntax restriction error. The message reads: "Do not override object.Finalize. Instead, provide a destructor."

In the .NET ecosystem, the System.Object class defines a method named Finalize() which is used by the Garbage Collector (GC) to clean up unmanaged resources before an object is destroyed. However, C# does not allow you to override this method directly using the override keyword. Instead, C# enforces the use of Destructor syntax (~ClassName), which the compiler automatically translates into a safe Finalize implementation.

This guide explains how to define finalization logic correctly in C#.

Understanding C# Destructors vs. Finalize

The reason C# forbids override void Finalize() is safety. When an object is finalized, it is critical that the base class's finalization logic also runs.

When you write a C# destructor:

~MyClass()
{
    // Cleanup code
}

The compiler implicitly converts it into this structure during compilation (IL generation):

protected override void Finalize()
{
    try
    {
        // Your Cleanup code
    }
    finally
    {
        base.Finalize(); // Guaranteed to execute
    }
}

By preventing manual overrides, C# guarantees that base.Finalize() is always called inside a finally block, preventing resource leaks that could occur if a developer forgot to call the base method or if an exception occurred.

Scenario: Attempting Manual Override

This error occurs when a developer familiar with other .NET languages (or looking at the Object Browser) tries to override the method directly.

Example of error:

using System;

public class ResourceManager
{
    // ⛔️ Error CS0249: Do not override object.Finalize. Instead, provide a destructor.
    // C# forbids accessing this method directly.
    protected override void Finalize()
    {
        Console.WriteLine("Cleaning up resources...");
        
        // Even calling base.Finalize() here is not enough to satisfy the compiler.
        base.Finalize();
    }
}

Solution: Use Destructor Syntax (~)

To define finalization logic, use the tilde ~ followed by the class name. This is the C# syntax for a destructor.

Example of error:

using System;

public class ResourceManager
{
    // ✅ Correct: Destructor syntax.
    // The compiler will generate the 'override Finalize' logic for you.
    ~ResourceManager()
    {
        Console.WriteLine("Cleaning up resources...");
    }
}

Best Practices: Do You Even Need This?

It is very rare to need a destructor in modern C# applications.

  1. Managed Resources: Do NOT use a destructor to release managed objects (like Lists, or other Classes). The GC handles those automatically.
  2. Unmanaged Resources: If you handle raw Windows APIs, pointers, or file handles, you should implement the Dispose Pattern using IDisposable.

If you have a destructor, you should almost always have IDisposable as well to allow developers to clean up manually.

public class SafeResource : IDisposable
{
    // 1. Implement Dispose for manual cleanup
    public void Dispose()
    {
        ReleaseUnmanagedResources();
        GC.SuppressFinalize(this); // Tell GC not to run the destructor
    }

    // 2. Use Destructor ONLY as a backup for unmanaged resources
    ~SafeResource()
    {
        ReleaseUnmanagedResources();
    }

    private void ReleaseUnmanagedResources()
    {
        // Free unmanaged memory/handles here
    }
}

Conclusion

CS0249 enforces the safety mechanism of the .NET Garbage Collector.

  1. Never write: override void Finalize().
  2. Write instead: ~ClassName().
  3. Even better: Implement IDisposable and only use the destructor as a fallback if you are dealing with non-memory resources (like direct OS handles).
Navigation