C# -

Primary Constructors

Primary constructors in C# are a feature introduced in C# 9.0 that allows you to define constructor parameters directly in the class declaration, simplifying the syntax and making the code more concise. This tutorial will cover the basics of primary constructors, how to use them, and provide best practices and examples.


1. Introduction to Primary Constructors

Primary constructors are a convenient way to define a constructor in a single line within the class definition. They can reduce boilerplate code and make classes more readable.


2. Basic Usage

Here's a simple example of a class with a primary constructor:

        
            using System;

namespace PrimaryConstructorExamples

public class Person(string name, int age)
{
    public void DisplayInfo()
    {
        Console.WriteLine($"Name: {name}, Age: {age}");
    }
}

class Program
{
    static void Main(string[] args)
    {
        Person person = new("Alice", 30);
        person.DisplayInfo();
    }
}

        
    

3. Accessing Constructor Parameters

Constructor parameters defined in a primary constructor can be used directly within the class body. Here's an example:

        
            using System;

namespace PrimaryConstructorExamples

public class Person(string name, int age)
{
    public void DisplayInfo()
    {
        Console.WriteLine($"Name: {name}, Age: {age}");
    }

    public void Greet()
    {
        Console.WriteLine($"Hello, {name}!");
    }
}

class Program
{
    static void Main(string[] args)
    {
        Person person = new("Bob", 25);
        person.DisplayInfo();
        person.Greet();
    }
}

        
    

4. Using Primary Constructors with Properties

You can use primary constructors to initialize properties directly. Here's an example:

        
            using System;

namespace PrimaryConstructorExamples

public class Person(string name, int age)
{
    public string Name { get; } = name;
    public int Age { get; } = age;

    public void DisplayInfo()
    {
        Console.WriteLine($"Name: {Name}, Age: {Age}");
    }
}

class Program
{
    static void Main(string[] args)
    {
        Person person = new("Charlie", 40);
        person.DisplayInfo();
    }
}

        
    

5. Inheritance and Primary Constructors

Primary constructors can also be used in derived classes. Here's an example:

        
            using System;

namespace PrimaryConstructorExamples

public class Person(string name, int age)
{
    public string Name { get; } = name;
    public int Age { get; } = age;

    public void DisplayInfo()
    {
        Console.WriteLine($"Name: {Name}, Age: {Age}");
    }
}

public class Employee(string name, int age, string position) : Person(name, age)
{
    public string Position { get; } = position;

    public void DisplayEmployeeInfo()
    {
        DisplayInfo();
        Console.WriteLine($"Position: {Position}");
    }
}

class Program
{
    static void Main(string[] args)
    {
        Employee employee = new("Dave", 35, "Manager");
        employee.DisplayEmployeeInfo();
    }
}

        
    

6. Primary Constructors and Default Values

You can provide default values for parameters in primary constructors. Here's an example:

        
            using System;

namespace PrimaryConstructorExamples

public class Person(string name = "Unknown", int age = 0)
{
    public string Name { get; } = name;
    public int Age { get; } = age;

    public void DisplayInfo()
    {
        Console.WriteLine($"Name: {Name}, Age: {Age}");
    }
}

class Program
{
    static void Main(string[] args)
    {
        Person person1 = new();
        Person person2 = new("Eve", 28);

        person1.DisplayInfo();
        person2.DisplayInfo();
    }
}

        
    

7. Combining Primary Constructors and Traditional Constructors

It's possible to combine primary constructors with traditional constructors. This can be useful for more complex initialization scenarios. Here's an example:

        
            using System;

namespace PrimaryConstructorExamples

public class Person(string name, int age)
{
    public string Name { get; } = name;
    public int Age { get; } = age;

    // Traditional constructor
    public Person(string name) : this(name, 0) { }

    public void DisplayInfo()
    {
        Console.WriteLine($"Name: {Name}, Age: {Age}");
    }
}

class Program
{
    static void Main(string[] args)
    {
        Person person1 = new("Frank");
        Person person2 = new("Grace", 32);

        person1.DisplayInfo();
        person2.DisplayInfo();
    }
}

        
    

8. Primary Constructors in Record Types

Primary constructors can also be used in record types, providing a convenient way to define immutable data objects. Here's an example:

        
            using System;

namespace PrimaryConstructorExamples

public record Person(string Name, int Age);

class Program
{
    static void Main(string[] args)
    {
        Person person = new("Hannah", 27);
        Console.WriteLine(person);
    }
}

        
    

9. Limitations of Primary Constructors

While primary constructors can simplify code, they have limitations. For example, they cannot contain logic and are less flexible than traditional constructors for complex initialization. Here's an example illustrating some limitations:

        
            using System;

namespace PrimaryConstructorExamples

public class Person(string name, int age)
{
    public string Name { get; } = name;
    public int Age { get; } = age;

    // Traditional constructor for complex initialization
    public Person(string name, int age, string address) : this(name, age)
    {
        Address = address;
    }

    public string Address { get; }

    public void DisplayInfo()
    {
        Console.WriteLine($"Name: {Name}, Age: {Age}, Address: {Address}");
    }
}

class Program
{
    static void Main(string[] args)
    {
        Person person = new("Ivy", 22, "123 Main St");
        person.DisplayInfo();
    }
}

        
    

10. Primary Constructors with Dependency Injection

Primary constructors can be used with dependency injection to simplify the injection of dependencies. Here's an example:

        
            using System;
using Microsoft.Extensions.DependencyInjection;

namespace PrimaryConstructorExamples

public interface IGreetingService
{
    void Greet(string name);
}

public class GreetingService : IGreetingService
{
    public void Greet(string name)
    {
        Console.WriteLine($"Hello, {name}!");
    }
}

public class Person(IGreetingService greetingService, string name)
{
    public void Greet()
    {
        greetingService.Greet(name);
    }
}

class Program
{
    static void Main(string[] args)
    {
        var serviceCollection = new ServiceCollection();
        serviceCollection.AddSingleton<IGreetingService, GreetingService>();
        var serviceProvider = serviceCollection.BuildServiceProvider();

        var person = new Person(serviceProvider.GetRequiredService<IGreetingService>(), "John");
        person.Greet();
    }
}

        
    

11. Best Practices for Using Primary Constructors

Here are some best practices to follow when using primary constructors in C#:


12. Conclusion

Primary constructors in C# provide a concise way to define constructors directly in the class declaration. By following best practices and understanding how to use primary constructors effectively, you can write cleaner and more maintainable code.