DevAcademia
C++C#CPythonJava
  • C# Basics

  • C# Introduction
  • C# Get Started
  • C# Syntax
  • C# Output
  • C# Comments
  • C# Variables
  • C# Data Types
  • C# Type Casting
  • C# User Input
  • C# Operators
  • C# Math
  • C# Strings
  • C# Booleans
  • C# If...Else
  • C# Switch Statement
  • C# While Loop
  • C# For Loop
  • C# Break and Continue
  • C# Arrays
  • C# Files
  • C# OOP

  • C# OOP Introduction
  • C# Classes and Objects
  • C# Class Members
  • C# Constructors
  • C# Destructors
  • C# Access Modifiers
  • C# Properties
  • C# Inheritance
  • C# Polymorphism
  • C# Abstraction
  • C# Interfaces
  • C# Enums
  • C# Exceptions
  • C# Quizzes

  • C# Quiz Introduction
  • C# Basics

  • C# Introduction
  • C# Get Started
  • C# Syntax
  • C# Output
  • C# Comments
  • C# Variables
  • C# Data Types
  • C# Type Casting
  • C# User Input
  • C# Operators
  • C# Math
  • C# Strings
  • C# Booleans
  • C# If...Else
  • C# Switch Statement
  • C# While Loop
  • C# For Loop
  • C# Break and Continue
  • C# Arrays
  • C# Files
  • C# OOP

  • C# OOP Introduction
  • C# Classes and Objects
  • C# Class Members
  • C# Constructors
  • C# Destructors
  • C# Access Modifiers
  • C# Properties
  • C# Inheritance
  • C# Polymorphism
  • C# Abstraction
  • C# Interfaces
  • C# Enums
  • C# Exceptions
  • C# Quizzes

  • C# Quiz Introduction

Loading Cs tutorial…

Loading content
C# OOPTopic 44 of 55
←PreviousPrevNextNext→

C# Destructors and Finalizers

Introduction to Destructors in C#

In C#, destructors (also called finalizers) are special methods that are automatically called when an object is being garbage collected. They provide a mechanism to perform cleanup operations for unmanaged resources before an object is destroyed.

Destructors are non-deterministic and rely on the .NET garbage collector for execution.

Destructor Syntax and Characteristics

- Destructors have the same name as the class preceded by a tilde (~)

- They cannot have access modifiers, parameters, or return types

- They cannot be called explicitly; only the garbage collector can invoke them

- Each class can have only one destructor

- They are inherited and called in derivation order (from most derived to base)

Basic Destructor Example

This example shows a simple class with a destructor that prints a message when the object is garbage collected.

Example
using System;

class SimpleClass
{
    private string name;
    
    public SimpleClass(string name)
    {
        this.name = name;
        Console.WriteLine($"Constructor called for {name}");
    }
    
    // Destructor (Finalizer)
    ~SimpleClass()
    {
        Console.WriteLine($"Destructor called for {name}");
    }
    
    public static void Main()
    {
        SimpleClass obj = new SimpleClass("TestObject");
        obj = null; // Make eligible for garbage collection
        
        // Force garbage collection (for demonstration only)
        GC.Collect();
        GC.WaitForPendingFinalizers();
        
        Console.WriteLine("Main method completed");
    }
}
Output
Constructor called for TestObject
Destructor called for TestObject
Main method completed

IDisposable Pattern for Deterministic Cleanup

C# provides the IDisposable interface for explicit, deterministic resource cleanup using the Dispose pattern.

Example
using System;

class ResourceHandler : IDisposable
{
    private string resourceName;
    private bool disposed = false;
    
    public ResourceHandler(string name)
    {
        resourceName = name;
        Console.WriteLine($"Resource acquired: {resourceName}");
    }
    
    public void UseResource()
    {
        if (disposed)
            throw new ObjectDisposedException(nameof(ResourceHandler));
        
        Console.WriteLine($"Using resource: {resourceName}");
    }
    
    // Public Dispose method (part of IDisposable)
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this); // Prevent finalizer from running
    }
    
    // Protected virtual Dispose method for inheritance
    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // Dispose managed resources
                Console.WriteLine($"Disposing managed resources: {resourceName}");
            }
            
            // Dispose unmanaged resources
            Console.WriteLine($"Cleaning up unmanaged resources: {resourceName}");
            
            disposed = true;
        }
    }
    
    // Destructor (backup for when Dispose isn't called)
    ~ResourceHandler()
    {
        Dispose(false);
    }
    
    public static void Main()
    {
        // Using statement ensures Dispose is called automatically
        using (var resource = new ResourceHandler("DatabaseConnection"))
        {
            resource.UseResource();
        } // Dispose() called automatically here
        
        Console.WriteLine("Resource cleanup completed");
    }
}
Output
Resource acquired: DatabaseConnection
Using resource: DatabaseConnection
Disposing managed resources: DatabaseConnection
Cleaning up unmanaged resources: DatabaseConnection
Resource cleanup completed

Using Statement for Automatic Cleanup

The using statement provides a convenient syntax for ensuring that Dispose() is called automatically, even if exceptions occur.

Example
using System;

class FileProcessor : IDisposable
{
    private string fileName;
    
    public FileProcessor(string name)
    {
        fileName = name;
        Console.WriteLine($"File opened: {fileName}");
    }
    
    public void ProcessFile()
    {
        Console.WriteLine($"Processing file: {fileName}");
    }
    
    public void Dispose()
    {
        Console.WriteLine($"File closed: {fileName}");
        // Actual file closing logic would go here
    }
    
    public static void Main()
    {
        // Multiple resources can be used in one using statement
        using (var file1 = new FileProcessor("data1.txt"),
               file2 = new FileProcessor("data2.txt"))
        {
            file1.ProcessFile();
            file2.ProcessFile();
        } // Both Dispose() methods called here
        
        // Alternative syntax for using statement
        using (var file3 = new FileProcessor("data3.txt"))
        {
            file3.ProcessFile();
        }
    }
}
Output
File opened: data1.txt
File opened: data2.txt
Processing file: data1.txt
Processing file: data2.txt
File closed: data2.txt
File closed: data1.txt
File opened: data3.txt
Processing file: data3.txt
File closed: data3.txt

Destructor Inheritance Chain

Destructors are called automatically in reverse order of inheritance (most derived first, then base classes).

Example
using System;

class BaseClass
{
    protected string className;
    
    public BaseClass(string name)
    {
        className = name;
        Console.WriteLine($"Base constructor: {className}");
    }
    
    ~BaseClass()
    {
        Console.WriteLine($"Base destructor: {className}");
    }
}

class DerivedClass : BaseClass
{
    public DerivedClass(string name) : base(name)
    {
        Console.WriteLine($"Derived constructor: {className}");
    }
    
    ~DerivedClass()
    {
        Console.WriteLine($"Derived destructor: {className}");
    }
}

class Program
{
    static void Main()
    {
        DerivedClass obj = new DerivedClass("TestObject");
        obj = null;
        
        GC.Collect();
        GC.WaitForPendingFinalizers();
    }
}
Output
Base constructor: TestObject
Derived constructor: TestObject
Derived destructor: TestObject
Base destructor: TestObject

Best Practices for Resource Management

- Prefer IDisposable and using statements over destructors for deterministic cleanup

- Implement the full Dispose pattern when managing unmanaged resources

- Only use destructors as a safety net when Dispose might not be called

- Call GC.SuppressFinalize(this) in Dispose() to avoid redundant cleanup

- Use using statements for short-lived resource ownership

- Avoid performing time-consuming operations in destructors

- Don't reference other managed objects in destructors (they may already be collected)

- Consider using SafeHandle for wrapping unmanaged resources

Comparison Table

FeatureDestructor (~Class)IDisposable Pattern
DeterministicNoYes
ControlGarbage CollectorDeveloper
PerformancePoorGood
ReliabilityLowHigh
InheritanceAutomatic chainingManual implementation
Recommended UseBackup cleanupPrimary resource management
Test your knowledge: C# Destructors and Finalizers
Quiz Configuration
4 of 8 questions
Sequential
Previous allowed
Review enabled
Early close allowed
Estimated time: 5 min
C# OOPTopic 44 of 55
←PreviousPrevNextNext→