Constructors

How to Create and Initialize Objects Properly

Intermediate 20 min read Lesson 4 of 11

Setting Up Your Objects Properly

In Lessons 1-3, we learned about classes, data, and methods. Now let's learn how to create objects correctly using Constructors!

Think of it like this:
  • Constructor = Instructions for setting up a new object
  • Like a factory machine that builds products with the right parts
  • Runs automatically when you create an object

Why are constructors important? They ensure every object starts with the correct data and valid state. Without them, objects could be created with missing or invalid data!

Step 1: What is a Constructor?

A constructor is a special method that runs when you create an object.

Constructor Basics

Key points about constructors:

  • ✅ Has the same name as the class
  • ✅ Has no return type (not even void)
  • ✅ Runs automatically when you use new
  • ✅ Used to initialize the object's data
public class Person
{
    public string Name;
    public int Age;
    
    // Constructor - same name as class!
    public Person()  // No return type
    {
        Name = "Unknown";  // Set default values
        Age = 0;
    }
}
Using a Constructor

The constructor runs when you create an object:

// Creating an object - constructor runs!
Person person = new Person();

// Now the object is properly initialized
Console.WriteLine(person.Name);  // "Unknown"
Console.WriteLine(person.Age);   // 0
Without a constructor: The object would have null or default values.
With a constructor: You control what values the object starts with!

Step 2: Default Constructor (No Parameters)

A default constructor has no parameters and sets default values.

Default Constructor Example
public class Product
{
    public string Name;
    public decimal Price;
    public int Quantity;
    
    // Default constructor
    public Product()
    {
        Name = "New Product";
        Price = 0.00m;
        Quantity = 0;
    }
}

// Usage
Product p = new Product();
Console.WriteLine(p.Name);     // "New Product"
Console.WriteLine(p.Price);    // 0.00
Important Note
If you create ANY constructor with parameters, C# does NOT automatically create a default constructor!
// This class has NO default constructor
public class Book
{
    public Book(string title)  // Only this constructor exists
    {
        Title = title;
    }
    
    public string Title { get; set; }
}

// ❌ This will NOT work:
// Book b = new Book();  // Error! No default constructor

// ✅ You must use the parameterized constructor:
Book b = new Book("C# Programming");

Step 3: Parameterized Constructors

A parameterized constructor takes inputs to set the object's data when created.

Parameterized Constructor
public class Student
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Course { get; set; }
    
    // Constructor with parameters
    public Student(string name, int age, string course)
    {
        Name = name;
        Age = age;
        Course = course;
    }
}

// Usage - set values when creating!
Student s = new Student("Alice", 20, "Computer Science");
Console.WriteLine(s.Name);    // "Alice"
Console.WriteLine(s.Age);     // 20
Benefits
  • Objects are ready to use immediately after creation
  • No missing data - all required values must be provided
  • Cleaner code - no need to set properties separately
Real-world example: When you create a new bank account, you must provide the owner's name and initial deposit.
BankAccount account = new BankAccount(
    "John Smith", 1000.00m);  // All set up!

Step 4: Constructor Overloading

Overloading means having multiple constructors with the same name but different parameters.

Overloaded Constructors
public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
    
    // Constructor 1: Default
    public Product()
    {
        Id = 0;
        Name = "Unknown";
        Price = 0.00m;
    }
    
    // Constructor 2: ID and Name only
    public Product(int id, string name)
    {
        Id = id;
        Name = name;
        Price = 0.00m;
    }
    
    // Constructor 3: All parameters
    public Product(int id, string name, decimal price)
    {
        Id = id;
        Name = name;
        Price = price;
    }
}
Using Different Constructors
// Uses constructor 1 (default)
Product p1 = new Product();
Console.WriteLine(p1.Name);  // "Unknown"

// Uses constructor 2 (ID + Name)
Product p2 = new Product(1, "Laptop");
Console.WriteLine(p2.Name);  // "Laptop"

// Uses constructor 3 (all parameters)
Product p3 = new Product(2, "Mouse", 25.50m);
Console.WriteLine(p3.Price); // 25.50
Why overload? It gives users flexibility - they can provide as much or as little information as they want.

Step 5: Constructor Chaining

Constructor chaining means one constructor calls another constructor to avoid code duplication.

Chaining Example
public class Employee
{
    public int EmployeeId { get; }
    public string Name { get; }
    public string Department { get; }
    public decimal Salary { get; }
    
    // Main constructor - does all the work
    public Employee(int id, string name, string dept, decimal salary)
    {
        EmployeeId = id;
        Name = name;
        Department = dept;
        Salary = salary;
    }
    
    // Chain to the main constructor with default department
    public Employee(int id, string name, decimal salary)
        : this(id, name, "General", salary)  // Calls main constructor
    {
    }
    
    // Chain with default department and salary
    public Employee(int id, string name)
        : this(id, name, "General", 30000)  // Calls main constructor
    {
    }
}
Benefits of Chaining
  • No code duplication - write initialization once
  • Easier maintenance - change in one place only
  • Cleaner code - less repetition
// Different ways to create an employee
Employee e1 = new Employee(1, "John", "IT", 45000);
Employee e2 = new Employee(2, "Jane", 35000);  // Department: General
Employee e3 = new Employee(3, "Bob");          // Dept: General, Salary: 30000
Tip: Use : this(...) to call another constructor in the same class.

Step 6: Static Constructors

A static constructor runs once when the class is first used.

Static Constructor
public class Configuration
{
    public static string AppName;
    public static string Version;
    public static DateTime StartTime;
    
    // Static constructor - runs once
    static Configuration()
    {
        AppName = "MyApplication";
        Version = "1.0.0";
        StartTime = DateTime.Now;
        Console.WriteLine("⚙️ Configuration initialized once!");
    }
}

// Usage - static constructor runs the first time
Configuration config = new Configuration();  // Static constructor runs
Console.WriteLine(Configuration.AppName);   // "MyApplication"
When to Use Static Constructors
  • One-time setup - load configuration once
  • Initialize static fields - shared data
  • Set up database connections - once per application
  • Register items - like product catalog
Important: Static constructors have no parameters and cannot be called directly. They run automatically when the class is first accessed.

Step 7: Private Constructors

A private constructor cannot be called from outside the class.

Private Constructor
public class Singleton
{
    private static Singleton instance;
    
    // Private constructor - nobody can create objects!
    private Singleton()
    {
        Console.WriteLine("🔒 Singleton created!");
    }
    
    // Public method to get the single instance
    public static Singleton GetInstance()
    {
        if (instance == null)
            instance = new Singleton();  // Only class can call private constructor
        return instance;
    }
}
Why Use Private Constructors?
  • Singleton Pattern - only one instance of a class
  • Utility Classes - only static methods (like Math)
  • Factory Pattern - control how objects are created
  • Prevent Instantiation - class is meant to be used statically
Real-world example: The Math class in C# has a private constructor because you only use its static methods like Math.Sqrt().

Step 8: Validating Input in Constructors

Always validate the data you receive in constructors!

Constructor with Validation
public class BankAccount
{
    public string Owner { get; }
    public decimal Balance { get; private set; }
    
    public BankAccount(string owner, decimal initialDeposit)
    {
        // ✅ Validate Owner
        if (string.IsNullOrWhiteSpace(owner))
            throw new ArgumentException("Owner name cannot be empty!");
        
        // ✅ Validate Initial Deposit
        if (initialDeposit < 0)
            throw new ArgumentException("Initial deposit cannot be negative!");
        
        Owner = owner;
        Balance = initialDeposit;
    }
}
Why Validate?
  • Prevents bad data - catch errors early
  • Saves debugging time - find issues at creation
  • Protects your code - avoid crashes later
  • Better user experience - clear error messages
// ❌ This will throw an error immediately
try
{
    BankAccount account = new BankAccount("", -100);
}
catch (Exception ex)
{
    Console.WriteLine($"Error: {ex.Message}");  // Catches the problem early!
}

Real-World Example: Library System

Let's build a complete LibraryItem class with all types of constructors:

LibraryItem.cs
using System;

public class LibraryItem
{
    // Properties
    public int ItemId { get; }
    public string Title { get; set; }
    public string Author { get; set; }
    public int Year { get; set; }
    public bool IsAvailable { get; private set; }
    
    // Static field for generating IDs
    private static int lastId = 1000;
    
    // Static constructor - runs once
    static LibraryItem()
    {
        Console.WriteLine("📚 Library System Initialized");
        lastId = 1000;
    }
    
    // Private constructor - used internally
    private LibraryItem()
    {
        IsAvailable = true;
    }
    
    // Main constructor with validation
    public LibraryItem(string title, string author, int year) 
        : this()  // Call private constructor
    {
        // Validate inputs
        if (string.IsNullOrWhiteSpace(title))
            throw new ArgumentException("Title cannot be empty!");
        
        if (string.IsNullOrWhiteSpace(author))
            throw new ArgumentException("Author cannot be empty!");
        
        if (year < 0 || year > DateTime.Now.Year)
            throw new ArgumentException("Invalid year!");
        
        ItemId = GenerateId();
        Title = title;
        Author = author;
        Year = year;
    }
    
    // Overloaded constructor - with ID (for loading from database)
    public LibraryItem(int id, string title, string author, int year, bool available)
    {
        ItemId = id;
        Title = title;
        Author = author;
        Year = year;
        IsAvailable = available;
    }
    
    // Private method to generate ID
    private static int GenerateId()
    {
        lastId++;
        return lastId;
    }
    
    // Methods
    public void Borrow()
    {
        if (!IsAvailable)
            throw new InvalidOperationException("Item is already borrowed!");
        
        IsAvailable = false;
        Console.WriteLine($"📖 '{Title}' borrowed.");
    }
    
    public void Return()
    {
        if (IsAvailable)
            throw new InvalidOperationException("Item is already available!");
        
        IsAvailable = true;
        Console.WriteLine($"📚 '{Title}' returned.");
    }
    
    public void DisplayInfo()
    {
        Console.WriteLine($"📚 Item #{ItemId}: {Title}");
        Console.WriteLine($"   Author: {Author}");
        Console.WriteLine($"   Year: {Year}");
        Console.WriteLine($"   Status: {(IsAvailable ? "✅ Available" : "❌ Borrowed")}");
        Console.WriteLine();
    }
}
Program.cs (Using the Library)
using System;

class Program
{
    static void Main()
    {
        Console.WriteLine("📚 LIBRARY MANAGEMENT SYSTEM");
        Console.WriteLine("═══════════════════════════════════\n");
        
        // Create items using different constructors
        LibraryItem book1 = new LibraryItem("C# Programming", "John Doe", 2020);
        LibraryItem book2 = new LibraryItem("Clean Code", "Robert Martin", 2008);
        LibraryItem book3 = new LibraryItem("Design Patterns", "Erich Gamma", 1994);
        
        // Display all items
        book1.DisplayInfo();
        book2.DisplayInfo();
        book3.DisplayInfo();
        
        // Borrow and return items
        Console.WriteLine("📖 Borrowing 'Clean Code'...");
        book2.Borrow();
        
        Console.WriteLine("📖 Borrowing 'Design Patterns'...");
        book3.Borrow();
        
        Console.WriteLine("\n📚 Updated Status:");
        book1.DisplayInfo();
        book2.DisplayInfo();
        book3.DisplayInfo();
        
        Console.WriteLine("📚 Returning 'Clean Code'...");
        book2.Return();
        
        Console.WriteLine("\n📚 Final Status:");
        book2.DisplayInfo();
    }
}
What this shows:
  • Default constructor (private)
  • Parameterized constructor with validation
  • Overloaded constructor for different scenarios
  • Static constructor for one-time setup
  • Private constructor for internal use
  • Constructor chaining with : this()

What You Learned Today

Default

Constructor with no parameters

Parameterized

Constructors with inputs

Overloading

Multiple constructors

Chaining

Constructors calling each other

Exercise: Create a Car Dealership System

Your Task:

Create a Car class with the following constructors and features:

Requirements:
  1. Properties:
    • VIN (string) - read-only
    • Make (string)
    • Model (string)
    • Year (int)
    • Price (decimal)
    • IsSold (bool) - private set
  2. Constructors:
    • Default constructor (sets default values)
    • Parameterized constructor (make, model, year, price)
    • Constructor with VIN (for loading from database)
More Requirements:
  1. Static Constructor:
    • Initialize a static counter for VIN generation
  2. Validation:
    • Year must be between 1886 and current year
    • Price must be greater than 0
    • Make and Model cannot be empty
  3. Methods:
    • Sell() - marks car as sold
    • DisplayInfo() - shows all car details
    • CalculateAge() - returns car's age
💡 Use the LibraryItem class as reference!
Test Your Knowledge - Take Quiz