What are constructors and destructors in C#?

10 minbeginnerOOPconstructorsdestructorsinitialization

Quick Answer

Constructors initialize objects when created. Types include default, parameterized, static, and private constructors. Destructors (finalizers) clean up resources when objects are garbage collected. Use constructors for initialization, destructors for cleanup. Prefer using statements and IDisposable over destructors for resource management.

Detailed Answer

Constructors are special methods that initialize objects when they are created, while destructors (finalizers) are special methods that clean up resources when objects are destroyed by the garbage collector.

Constructor Types:

1. Default Constructor

  • Parameterless constructor
  • Automatically provided if no constructors are defined
  • Initializes fields to default values

2. Parameterized Constructor

  • Takes parameters to initialize the object
  • Allows custom initialization

3. Copy Constructor

  • Creates a new object by copying another object
  • Useful for creating deep copies

4. Static Constructor

  • Initializes static members
  • Called once before the class is first used

Example:

public class Person
{
    // Fields
    private string name;
    private int age;
    private DateTime birthDate;
    private static int totalPersons = 0;
    
    // Static constructor - called once when class is first used
    static Person()
    {
        Console.WriteLine("Person class initialized");
        totalPersons = 0;
    }
    
    // Default constructor
    public Person()
    {
        Console.WriteLine("Default constructor called");
        name = "Unknown";
        age = 0;
        birthDate = DateTime.MinValue;
        totalPersons++;
    }
    
    // Parameterized constructor
    public Person(string name, int age)
    {
        Console.WriteLine($"Parameterized constructor called for {name}");
        this.name = name;
        this.age = age;
        this.birthDate = DateTime.Now.AddYears(-age);
        totalPersons++;
    }
    
    // Copy constructor
    public Person(Person other)
    {
        Console.WriteLine($"Copy constructor called for {other.name}");
        this.name = other.name;
        this.age = other.age;
        this.birthDate = other.birthDate;
        totalPersons++;
    }
    
    // Constructor chaining using 'this'
    public Person(string name) : this(name, 0)
    {
        Console.WriteLine("Constructor chaining - calling parameterized constructor");
    }
    
    // Properties
    public string Name
    {
        get => name;
        set => name = value ?? throw new ArgumentNullException(nameof(value));
    }
    
    public int Age
    {
        get => age;
        set => age = value >= 0 ? value : throw new ArgumentException("Age cannot be negative");
    }
    
    public static int TotalPersons => totalPersons;
    
    // Methods
    public void DisplayInfo()
    {
        Console.WriteLine($"Name: {name}, Age: {age}, Born: {birthDate:yyyy-MM-dd}");
    }
    
    // Destructor (Finalizer) - called by garbage collector
    ~Person()
    {
        Console.WriteLine($"Destructor called for {name}");
        totalPersons--;
    }
}

// Advanced example with resource management
public class FileManager : IDisposable
{
    private string fileName;
    private FileStream fileStream;
    private bool disposed = false;
    
    // Constructor with file validation
    public FileManager(string fileName)
    {
        if (string.IsNullOrEmpty(fileName))
            throw new ArgumentException("File name cannot be null or empty");
        
        this.fileName = fileName;
        Console.WriteLine($"FileManager created for: {fileName}");
    }
    
    // Method to open file
    public void OpenFile()
    {
        if (fileStream != null)
            throw new InvalidOperationException("File is already open");
        
        try
        {
            fileStream = File.Open(fileName, FileMode.OpenOrCreate);
            Console.WriteLine($"File opened: {fileName}");
        }
        catch (Exception ex)
        {
            throw new InvalidOperationException($"Failed to open file: {ex.Message}");
        }
    }
    
    // Method to write data
    public void WriteData(string data)
    {
        if (fileStream == null)
            throw new InvalidOperationException("File is not open");
        
        byte[] bytes = Encoding.UTF8.GetBytes(data);
        fileStream.Write(bytes, 0, bytes.Length);
        fileStream.Flush();
        Console.WriteLine($"Data written to {fileName}");
    }
    
    // Destructor - backup cleanup (not guaranteed to be called)
    ~FileManager()
    {
        Console.WriteLine($"Destructor called for {fileName}");
        Dispose(false);
    }
    
    // IDisposable implementation for proper resource cleanup
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this); // Prevents destructor from being called
    }
    
    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // Dispose managed resources
                fileStream?.Dispose();
                Console.WriteLine($"File closed: {fileName}");
            }
            
            // Dispose unmanaged resources (if any)
            disposed = true;
        }
    }
}

Key Points:

Constructors:

  • Must have the same name as the class
  • Cannot have a return type
  • Can be overloaded
  • Can call other constructors using this or base
  • Static constructors cannot have access modifiers

Destructors:

  • Cannot be called explicitly
  • Cannot be overloaded or inherited
  • Cannot have access modifiers
  • Should not be relied upon for critical cleanup
  • Use IDisposable pattern for proper resource management

Best Practices:

  • Use constructors for initialization
  • Use IDisposable instead of destructors for resource cleanup
  • Implement proper exception handling in constructors
  • Use constructor chaining to avoid code duplication
  • Prefer using statements for automatic disposal