Constructors
How to Create and Initialize Objects Properly
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!
- 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
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
// 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
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
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
: 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
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
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();
}
}
- ✅ 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:
-
Properties:
VIN(string) - read-onlyMake(string)Model(string)Year(int)Price(decimal)IsSold(bool) - private set
-
Constructors:
- Default constructor (sets default values)
- Parameterized constructor (make, model, year, price)
- Constructor with VIN (for loading from database)
More Requirements:
-
Static Constructor:
- Initialize a static counter for VIN generation
-
Validation:
- Year must be between 1886 and current year
- Price must be greater than 0
- Make and Model cannot be empty
-
Methods:
Sell()- marks car as soldDisplayInfo()- shows all car detailsCalculateAge()- returns car's age
// Car.cs
using System;
public class Car
{
// Properties
public string VIN { get; private set; }
public string Make { get; set; }
public string Model { get; set; }
public int Year { get; set; }
public decimal Price { get; set; }
public bool IsSold { get; private set; }
// Static counter for VIN generation
private static int vinCounter = 1000;
// Static constructor
static Car()
{
Console.WriteLine("🚗 Car Dealership System Initialized");
vinCounter = 1000;
}
// Default constructor
public Car()
{
VIN = GenerateVIN();
Make = "Unknown";
Model = "Unknown";
Year = DateTime.Now.Year;
Price = 0.00m;
IsSold = false;
}
// Parameterized constructor
public Car(string make, string model, int year, decimal price)
{
// Validate
if (string.IsNullOrWhiteSpace(make))
throw new ArgumentException("Make cannot be empty!");
if (string.IsNullOrWhiteSpace(model))
throw new ArgumentException("Model cannot be empty!");
if (year < 1886 || year > DateTime.Now.Year)
throw new ArgumentException("Invalid year!");
if (price <= 0)
throw new ArgumentException("Price must be greater than 0!");
VIN = GenerateVIN();
Make = make;
Model = model;
Year = year;
Price = price;
IsSold = false;
}
// Constructor with VIN (for database loading)
public Car(string vin, string make, string model, int year, decimal price, bool sold)
{
VIN = vin;
Make = make;
Model = model;
Year = year;
Price = price;
IsSold = sold;
}
// Private method to generate VIN
private static string GenerateVIN()
{
vinCounter++;
return $"VIN-{vinCounter:D6}";
}
// Methods
public void Sell()
{
if (IsSold)
throw new InvalidOperationException("Car is already sold!");
IsSold = true;
Console.WriteLine($"💵 '{Make} {Model}' sold for ${Price:F2}!");
}
public int CalculateAge()
{
return DateTime.Now.Year - Year;
}
public void DisplayInfo()
{
Console.WriteLine($"🚗 VIN: {VIN}");
Console.WriteLine($" Make/Model: {Make} {Model}");
Console.WriteLine($" Year: {Year} (Age: {CalculateAge()} years)");
Console.WriteLine($" Price: ${Price:F2}");
Console.WriteLine($" Status: {(IsSold ? "❌ Sold" : "✅ Available")}");
Console.WriteLine();
}
}
// Program.cs
class Program
{
static void Main()
{
Console.WriteLine("🚗 CAR DEALERSHIP SYSTEM");
Console.WriteLine("═══════════════════════════════════\n");
// Create cars using different constructors
Car car1 = new Car("Toyota", "Camry", 2022, 28000.00m);
Car car2 = new Car("Honda", "Civic", 2023, 25000.00m);
Car car3 = new Car("Ford", "Mustang", 2021, 35000.00m);
// Display all cars
car1.DisplayInfo();
car2.DisplayInfo();
car3.DisplayInfo();
// Sell a car
Console.WriteLine("💵 Selling Toyota Camry...");
car1.Sell();
Console.WriteLine();
car1.DisplayInfo();
// Try to sell an already sold car
try
{
car1.Sell();
}
catch (Exception ex)
{
Console.WriteLine($"❌ Error: {ex.Message}");
}
Console.WriteLine("\n📊 Total cars in inventory:");
Console.WriteLine($" {car1.Make} {car1.Model}: {(car1.IsSold ? "Sold" : "Available")}");
Console.WriteLine($" {car2.Make} {car2.Model}: {(car2.IsSold ? "Sold" : "Available")}");
Console.WriteLine($" {car3.Make} {car3.Model}: {(car3.IsSold ? "Sold" : "Available")}");
}
}