Skip to main content

How to Resolve Error "CS0249: Do not override object.Finalize. Instead, provide a destructor." in C#

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...");
}
}
note

Destructors cannot have access modifiers (public, private), cannot take parameters, and cannot be called explicitly. They are called non-deterministically by the Garbage Collector.

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).