Skip to main content

C# Constructors

C# Constructors Banner

When you create a new object in C#, there's a special method that runs automatically to set up that object. This special method is called a constructor. Constructors are fundamental to object-oriented programming in C# and understanding how they work is essential for writing effective C# code.

What is a Constructor?

A constructor is a special method in a class that is automatically called when an instance of the class is created. Its primary purpose is to initialize the object's data members and allocate resources. Constructors have the same name as the class and do not have a return type (not even void).

Types of Constructors in C#

C# supports several types of constructors:

  1. Default Constructor
  2. Parameterized Constructor
  3. Static Constructor
  4. Copy Constructor
  5. Private Constructor

Let's explore each of these in detail.

Default Constructor

A default constructor is one that either has no parameters, or if it has parameters, all of them have default values. If you don't provide any constructor in your class, C# automatically creates a default constructor that initializes all fields to their default values.

csharp
public class Person
{
public string Name;
public int Age;

// Default constructor
public Person()
{
Name = "Unknown";
Age = 0;
}
}

// Using the default constructor
Person person = new Person();
Console.WriteLine($"Name: {person.Name}, Age: {person.Age}");

Output:

Name: Unknown, Age: 0

Parameterized Constructor

A parameterized constructor accepts parameters that are used to initialize the object's properties. This allows you to set specific values for an object when it's created.

csharp
public class Person
{
public string Name;
public int Age;

// Parameterized constructor
public Person(string name, int age)
{
Name = name;
Age = age;
}
}

// Using the parameterized constructor
Person person = new Person("John Doe", 30);
Console.WriteLine($"Name: {person.Name}, Age: {person.Age}");

Output:

Name: John Doe, Age: 30

Constructor Overloading

You can define multiple constructors in a class with different parameter lists. This is known as constructor overloading. The compiler determines which constructor to call based on the arguments provided.

csharp
public class Person
{
public string Name;
public int Age;

// Default constructor
public Person()
{
Name = "Unknown";
Age = 0;
}

// Parameterized constructor with name only
public Person(string name)
{
Name = name;
Age = 0;
}

// Parameterized constructor with name and age
public Person(string name, int age)
{
Name = name;
Age = age;
}
}

// Using different constructors
Person person1 = new Person();
Person person2 = new Person("Jane Doe");
Person person3 = new Person("John Doe", 30);

Console.WriteLine($"Person1: {person1.Name}, {person1.Age}");
Console.WriteLine($"Person2: {person2.Name}, {person2.Age}");
Console.WriteLine($"Person3: {person3.Name}, {person3.Age}");

Output:

Person1: Unknown, 0
Person2: Jane Doe, 0
Person3: John Doe, 30

Static Constructor

A static constructor is used to initialize static members of a class. It is called automatically before the first instance is created or any static members are referenced. A class can have only one static constructor, and it cannot have parameters.

csharp
public class DatabaseConnection
{
public static string ConnectionString;

// Static constructor
static DatabaseConnection()
{
ConnectionString = "Server=myServerAddress;Database=myDataBase;User Id=myUsername;Password=myPassword;";
Console.WriteLine("Static constructor called");
}

// Instance constructor
public DatabaseConnection()
{
Console.WriteLine("Instance constructor called");
}
}

// Static constructor is called when the class is first accessed
Console.WriteLine("Before accessing the class");
Console.WriteLine($"Connection String: {DatabaseConnection.ConnectionString}");
Console.WriteLine("Creating an instance");
DatabaseConnection db = new DatabaseConnection();

Output:

Before accessing the class
Static constructor called
Connection String: Server=myServerAddress;Database=myDataBase;User Id=myUsername;Password=myPassword;
Creating an instance
Instance constructor called

Copy Constructor

A copy constructor creates a new object by copying variables from another object of the same class. This is useful when you need to create a new object with the same state as an existing object.

csharp
public class Person
{
public string Name;
public int Age;

// Parameterized constructor
public Person(string name, int age)
{
Name = name;
Age = age;
}

// Copy constructor
public Person(Person person)
{
Name = person.Name;
Age = person.Age;
}
}

// Using the copy constructor
Person original = new Person("John Doe", 30);
Person copy = new Person(original);

copy.Age = 31; // Modifying the copy doesn't affect the original

Console.WriteLine($"Original: {original.Name}, {original.Age}");
Console.WriteLine($"Copy: {copy.Name}, {copy.Age}");

Output:

Original: John Doe, 30
Copy: John Doe, 31

Private Constructor

A private constructor is only accessible within the class itself. It's commonly used in classes that contain only static members or in implementing the Singleton design pattern.

csharp
public class Singleton
{
private static Singleton _instance;

// Private constructor
private Singleton()
{
Console.WriteLine("Instance created");
}

public static Singleton GetInstance()
{
if (_instance == null)
{
_instance = new Singleton();
}
return _instance;
}
}

// You can't create an instance directly because constructor is private
// Singleton instance = new Singleton(); // This would cause a compile error

// Instead, use the GetInstance method
Console.WriteLine("Getting first instance");
Singleton instance1 = Singleton.GetInstance();
Console.WriteLine("Getting second instance");
Singleton instance2 = Singleton.GetInstance();

Console.WriteLine($"Are instances the same object? {object.ReferenceEquals(instance1, instance2)}");

Output:

Getting first instance
Instance created
Getting second instance
Are instances the same object? True

Constructor Chaining

Constructor chaining is a technique where one constructor calls another constructor in the same class. This is done using the this keyword.

csharp
public class Person
{
public string Name;
public int Age;
public string Address;

// Main constructor
public Person(string name, int age, string address)
{
Name = name;
Age = age;
Address = address;
}

// Constructor that calls another constructor
public Person(string name, int age) : this(name, age, "Unknown")
{
}

// Another constructor that calls another constructor
public Person(string name) : this(name, 0)
{
}
}

Person person1 = new Person("John");
Person person2 = new Person("Jane", 25);
Person person3 = new Person("Bob", 40, "123 Main St");

Console.WriteLine($"{person1.Name}, {person1.Age}, {person1.Address}");
Console.WriteLine($"{person2.Name}, {person2.Age}, {person2.Address}");
Console.WriteLine($"{person3.Name}, {person3.Age}, {person3.Address}");

Output:

John, 0, Unknown
Jane, 25, Unknown
Bob, 40, 123 Main St

Real-World Example: Building a Product Class

Let's create a more practical example of constructors by building a Product class for an e-commerce application:

csharp
public class Product
{
// Properties
public string Id { get; private set; }
public string Name { get; set; }
public decimal Price { get; set; }
public int StockQuantity { get; set; }
public DateTime CreatedAt { get; private set; }

// Static property to track total products created
private static int TotalProducts = 0;

// Static constructor
static Product()
{
Console.WriteLine("Product tracking system initialized");
}

// Default constructor
public Product()
{
Id = GenerateId();
Name = "Unnamed Product";
Price = 0;
StockQuantity = 0;
CreatedAt = DateTime.Now;
TotalProducts++;
}

// Parameterized constructor
public Product(string name, decimal price, int stockQuantity)
{
Id = GenerateId();
Name = name;
Price = price;
StockQuantity = stockQuantity;
CreatedAt = DateTime.Now;
TotalProducts++;
}

// Copy constructor
public Product(Product original)
{
Id = GenerateId(); // New products always get a new ID
Name = original.Name + " (Copy)";
Price = original.Price;
StockQuantity = original.StockQuantity;
CreatedAt = DateTime.Now;
TotalProducts++;
}

// Helper method to generate unique ID
private string GenerateId()
{
return $"PROD-{Guid.NewGuid().ToString().Substring(0, 8).ToUpper()}";
}

// Method to get total product count
public static int GetTotalProducts()
{
return TotalProducts;
}

// Method to display product info
public void DisplayInfo()
{
Console.WriteLine($"Product ID: {Id}");
Console.WriteLine($"Name: {Name}");
Console.WriteLine($"Price: ${Price}");
Console.WriteLine($"In Stock: {StockQuantity}");
Console.WriteLine($"Created: {CreatedAt}");
Console.WriteLine("--------------------");
}
}

// Using the Product class
Console.WriteLine($"Starting product count: {Product.GetTotalProducts()}");

Product laptop = new Product("Laptop", 1299.99m, 10);
laptop.DisplayInfo();

Product defaultProduct = new Product();
defaultProduct.DisplayInfo();

Product laptopCopy = new Product(laptop);
laptopCopy.DisplayInfo();

Console.WriteLine($"Final product count: {Product.GetTotalProducts()}");

Output:

Product tracking system initialized
Starting product count: 0
Product ID: PROD-F7A3B2C1
Name: Laptop
Price: $1299.99
In Stock: 10
Created: 2023-06-15 14:30:22
--------------------
Product ID: PROD-A1E5D7B9
Name: Unnamed Product
Price: $0
In Stock: 0
Created: 2023-06-15 14:30:22
--------------------
Product ID: PROD-C9D4E2F1
Name: Laptop (Copy)
Price: $1299.99
In Stock: 10
Created: 2023-06-15 14:30:22
--------------------
Final product count: 3

Constructor Guidelines and Best Practices

  1. Keep constructors simple: Constructors should primarily focus on initializing the object's state.

  2. Initialize all fields: Make sure all fields are properly initialized in your constructors.

  3. Don't call virtual methods in constructors: Virtual methods can be overridden in derived classes, which could cause unexpected behavior when called from a constructor.

  4. Use constructor chaining: Avoid duplicate code by having constructors call each other.

  5. Consider using factory methods: For complex object creation, consider implementing factory methods instead of multiple constructors.

  6. Follow naming conventions: Constructors should always have the same name as the class.

  7. Use object initializers for optional properties: For classes with many properties where only a few need to be set, consider using object initializers instead of multiple constructors.

Summary

Constructors are special methods in C# that allow you to initialize objects when they are created. They have the same name as the class and don't have a return type. C# supports various types of constructors including default, parameterized, static, copy, and private constructors.

Understanding constructors is crucial for proper object initialization in C#. They provide a way to ensure that objects are always in a valid state from the moment they are created, and they help enforce encapsulation by controlling how objects are initialized.

Exercises

  1. Create a BankAccount class with constructors that initialize the account number, owner name, and balance. Include a static constructor that sets the bank name.

  2. Implement a Rectangle class with constructors for different ways to create a rectangle (default, by width/height, by corners).

  3. Create a Car class with constructor chaining that allows creating cars with different levels of details (model only, model and year, full specifications).

  4. Implement the Singleton pattern using a private constructor.

  5. Build a Student class with a copy constructor and explain why deep copying might be necessary for reference type properties.

Additional Resources



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)