A destructor in C# is a special member that is used to clean up unmanaged resources before an object is removed from memory. In modern C#, destructors are more accurately called finalizers because they are controlled by the garbage collector.
Most beginner C# programs do not need destructors. The .NET garbage collector automatically releases managed memory. But when a class directly owns unmanaged resources such as native handles, operating system handles, unmanaged memory, or hardware resources, cleanup becomes important.
A destructor in C# looks similar to a C++ destructor, but its behavior is very different. In C#, you cannot decide exactly when a destructor runs. The garbage collector decides when finalization happens.
What Is Destructor in C#?
A destructor is a special member that has the same name as the class, starts with a tilde symbol, and has no parameters or return type. It is called by the garbage collector before the object is finally removed from memory.
class ResourceHolder
{
~ResourceHolder()
{
Console.WriteLine("Destructor called");
}
}
This destructor does not run immediately when the object goes out of scope. It runs later, when the garbage collector decides the object is unreachable and ready for finalization.
Destructor Syntax in C#
class ClassName
{
~ClassName()
{
// cleanup code
}
}
- The destructor name must match the class name.
- It must start with the tilde symbol
~. - It cannot have parameters.
- It cannot have a return type.
- It cannot be called manually.
- It cannot use access modifiers such as
publicorprivate.
Destructor Example in C#
The following example shows a simple class with a constructor and destructor.
class Demo
{
public Demo()
{
Console.WriteLine("Object created");
}
~Demo()
{
Console.WriteLine("Object destroyed");
}
}
Demo demo = new Demo();
The constructor runs immediately when new Demo() is executed. The destructor may run much later. In a short console program, you may not even see the destructor output before the process ends.
How Destructor Works with Garbage Collector
C# uses automatic memory management. When an object is no longer reachable, the garbage collector can reclaim its memory. If the object has a destructor, the cleanup process becomes more expensive because the object must be placed on a finalization queue before memory is reclaimed.
- The object becomes unreachable from active code.
- The garbage collector detects the unreachable object.
- If the object has a destructor, it is queued for finalization.
- The finalizer thread runs the destructor later.
- The object memory is reclaimed in a later garbage collection cycle.
This means destructors delay cleanup compared to normal garbage collection. An object with a finalizer usually survives at least one extra collection cycle. That is one reason destructors should be used only when required.
Destructor vs Constructor in C#
| Point | Constructor | Destructor |
|---|---|---|
| Purpose | Initializes an object | Cleans unmanaged resources |
| When It Runs | During object creation | Before final garbage collection |
| Call | Called using new | Called by garbage collector |
| Parameters | Can have parameters | Cannot have parameters |
| Overloading | Can be overloaded | Cannot be overloaded |
| Access Modifier | Can use access modifiers | Cannot use access modifiers |
The constructor prepares the object for use. The destructor acts as a last chance cleanup mechanism. These two members appear related in syntax, but they solve opposite lifecycle problems.
Destructor vs Dispose in C#
A destructor is not deterministic. You do not know when it will run. The Dispose method from the IDisposable interface is deterministic because the developer can call it directly or use a using statement.
class FileManager : IDisposable
{
public void Dispose()
{
Console.WriteLine("Resources released");
}
}
using FileManager manager = new FileManager();
When the using scope ends, Dispose is called automatically. This is usually better than waiting for a destructor because files, handles, locks, and connections should be released as soon as possible.
When Should You Use Destructor?
Use a destructor only when a class directly owns unmanaged resources and needs a safety net in case Dispose is not called. If your class only uses managed objects, you usually do not need a destructor.
- Use destructor for unmanaged resource cleanup safety.
- Do not use destructor for normal managed memory cleanup.
- Prefer
IDisposablefor predictable cleanup. - Prefer
SafeHandlefor native handles when possible. - Avoid expensive logic inside destructors.
Dispose Pattern with Destructor
When unmanaged resources are involved, a common pattern is to implement IDisposable and also provide a destructor as a backup. The normal path is Dispose. The destructor is only the emergency path if the developer forgets to dispose the object.
class NativeResource : IDisposable
{
private bool disposed;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposed)
{
return;
}
if (disposing)
{
// release managed resources here
}
// release unmanaged resources here
disposed = true;
}
~NativeResource()
{
Dispose(false);
}
}
GC.SuppressFinalize(this) tells the garbage collector that finalization is no longer needed because cleanup already happened through Dispose. This avoids unnecessary finalizer work and improves performance.
Why Destructors Should Be Used Carefully
Destructors add overhead. Objects with finalizers usually take longer to clean up because they need special finalization handling. Also, destructor execution order is not guaranteed. If one finalizer depends on another object that has already been finalized, bugs can appear.
Another problem is exception handling. A destructor should not throw exceptions. Finalizer thread exceptions can cause serious reliability issues. Keep destructor logic short, safe, and focused only on cleanup.
Managed and Unmanaged Resources
Managed resources are objects controlled by the .NET runtime, such as normal C# objects, strings, arrays, lists, and most framework objects. The garbage collector can clean their memory automatically when they are no longer used.
Unmanaged resources are resources outside direct garbage collector control. Examples include native memory, raw file handles, window handles, sockets, database handles, and handles returned by operating system APIs. These resources may need explicit release.
If your class only contains managed objects, do not write a destructor just to clear them. The garbage collector already handles managed memory. A destructor should exist only when there is unmanaged cleanup that cannot be handled more safely by another type.
SafeHandle and Modern Resource Cleanup
In modern C#, many unmanaged handle scenarios should use SafeHandle or existing .NET wrapper classes instead of manually writing a finalizer. SafeHandle is designed to release native handles reliably and reduces the chance of writing finalization code incorrectly.
This is why many application developers rarely write destructors directly. They use FileStream, SqlConnection, HttpClient, or other disposable framework classes, and they make sure those objects are disposed correctly.
Common Mistakes with Destructor in C#
- Using destructors to clean normal managed objects.
- Expecting the destructor to run immediately after an object goes out of scope.
- Putting business logic inside a destructor.
- Throwing exceptions from a destructor.
- Forgetting to implement
IDisposablewhen deterministic cleanup is needed. - Not calling
GC.SuppressFinalizeafter successful disposal.
The biggest beginner mistake is thinking a destructor is the same as deleting an object manually. C# does not work like that. You do not control the exact destruction time, and you should not depend on finalizers for ordinary program flow.
Best Practices for Destructor in C#
- Avoid destructors unless unmanaged resources are directly owned.
- Use
IDisposablefor predictable cleanup. - Use
usingstatements with disposable objects. - Keep destructor code minimal and safe.
- Do not depend on destructor execution order.
- Use
SafeHandlewhere possible instead of manually writing finalizers.
Destructor Interview Points
For interviews, remember that a destructor in C# is compiled into a finalizer. It is called by the garbage collector, not by the programmer. It cannot be overloaded, cannot have parameters, cannot have access modifiers, and cannot be called directly.
Also remember that destructors are mainly related to unmanaged resource cleanup. For most real cleanup work, IDisposable and using are preferred because they provide predictable release timing.
Why Most C# Classes Do Not Need Destructors
Most classes in C# only store managed data such as strings, numbers, arrays, lists, objects, and other framework types. The garbage collector already knows how to reclaim memory for these objects. Adding a destructor to such a class does not make cleanup better. It usually makes cleanup slower because the object must go through finalization.
If your class uses another disposable object, such as a stream or database connection, the class should usually implement IDisposable and dispose that object at the correct time. It still does not automatically mean your class needs a destructor. A destructor is only useful when there is direct unmanaged ownership that may leak if final cleanup never happens.
Simple Rule for Destructor Usage
If you are unsure whether a class needs a destructor, it probably does not. Start with normal managed code. Use IDisposable when cleanup must happen predictably. Consider a destructor only when unmanaged resources are directly owned and there is no safer wrapper already available.
This is especially important in long-running applications such as web servers, desktop tools, background workers, and device monitoring software. Resource leaks in these programs may not appear immediately, but over time they can cause locked files, exhausted handles, slow performance, or unexpected crashes.
Clean ownership rules are more important than adding finalizers everywhere.
That approach keeps memory management predictable, readable, and easier to debug in production systems.
It also avoids unnecessary garbage collector pressure.
Prefer simple cleanup paths.
FAQs on Destructor in C#
Is destructor called automatically in C#?
Yes. A destructor is called automatically by the garbage collector, but the exact time is not guaranteed.
Can destructor be overloaded in C#?
No. A class can have only one destructor, and it cannot accept parameters.
Should I use destructor or Dispose?
Use Dispose for predictable cleanup. Use a destructor only as a safety net for unmanaged resources when needed.
Can a destructor be public or private?
No. A destructor cannot have an access modifier. The garbage collector controls finalization.