A Deep Dive Into Primary Constructors

Bora Kaşmer
4 min readSep 19, 2024

--

Hi,
Today we will talk about Primary Constructor, which came with C# 12 .Net. It is used to simplify the syntax for initializing properties in classes.

Before The Primary Constructors

As usual, we assign the relevant properties to the incoming parameters in the class constructor. However, as the number of parameters increases, this reduces code readability and leads to unnecessary duplication.

namespace PrimaryConstructor.Models
{
public class User
{
public User(string name, string surname)
{
this.Name = name;
this.Surname = surname;
}
public string Name { get; set; }
public string Surname { get; set; }
}
}

“Simplicity and repose are the qualities that measure the true value of any work of art.”

— Frank Lloyd Wright

After The Primary Constructors

We will accept parameters with the class, as shown below, without using a constructor. In this sense, a class can be thought of as similar to a method, but without a return type.

namespace PrimaryConstructor.Models
{
public class User(string name, string surname)
{
public string Surname => surname;
public string FullName => $"{Name} {Surname}";

public void UserInfo()
{
System.Diagnostics.Debug.WriteLine($"Full Name: {FullName}");
System.Diagnostics.Debug.WriteLine($"Surname: {Surname}");
}
}
}
Overloading

Overload Constructors

In the example below, an additional option was added when creating the User class. If desired, you can provide just the First Name and Surname, or optionally, the Date of Birth as well, during the initial creation of the User.

In the image below, the first name and surname parameters passed to the User constructor are forwarded to the primary constructor of the User class using this keyword. The BirthDate parameter is assigned directly to its corresponding property in the overloaded User constructor.

this(name, surname)
namespace PrimaryConstructor.Models
{
public class User(string name, string surname)
{
public User(string name, string surname, DateTime? birthDate) : this(name, surname) { BirthDate = birthDate; }
public string Name => name;
public string Surname => surname;
public DateTime? BirthDate { get; set; }
public int? Age => BirthDate.HasValue ? (int?)((DateTime.Now - BirthDate.Value).Days / 365) : null;
public string FullName => $"{Name} {Surname}";

public void UserInfo()
{
System.Diagnostics.Debug.WriteLine($"Full Name: {FullName}");
System.Diagnostics.Debug.WriteLine($"Surname: {Surname}");
System.Diagnostics.Debug.WriteLine($"Birth Date: {BirthDate}");
System.Diagnostics.Debug.WriteLine($"Age: {Age}");
}
}
}

Usage Example: We created a new User with Overload Constructer and printed UserInfo to the output window.

 [HttpGet(Name = "GetUser")]
public User Get()
{
var model = new User("Bora", "Kasmer", new DateTime(1978, 6, 3));
//var model = new User("Bora", "Kasmer");
model.UserInfo();
return model;
}

Result:

Result of User Info

Dependency Injection Repository Layer

Especially in .NET Web API projects, services are often passed as parameters in class constructors via dependency injection. At this point, Primary Constructors greatly simplify the code.

Repository

IUserRepository: We created an IUserRepository interface to add a repository to the project using dependency injection.

using PrimaryConstructor.Models;

namespace PrimaryConstructor.Repository
{
public interface IUserRepository
{
IEnumerable<User> GetUsers();
User? GetUserByName(string name);
}
}

UserRepository: In the example below, the Repository layer was created with dummy data for demonstration purposes. Methods for retrieving user information and finding a user by their first or last name are simulated for illustration.

using PrimaryConstructor.Models;

namespace PrimaryConstructor.Repository
{
public class UserRepository() : IUserRepository
{
private static readonly List<User> _users = new List<User> {
new User("John", "Doe", new DateTime(1990, 1, 1)),
new User("Jane", "Doe", new DateTime(1995, 1, 1)),
new User("Alice", "Smith", new DateTime(2000, 1, 1)),
new User("Bob", "Smith", new DateTime(2005, 1, 1)),
new User("Charlie", "Brown", new DateTime(2010, 1, 1)),
new User("Daisy", "Brown", new DateTime(2015, 1, 1))
};
public IEnumerable<User> GetUsers()
{
return _users;
}

public User? GetUserByName(string name)
{
return _users.FirstOrDefault(u => u.Name.ToUpper().Contains(name.ToUpper()) || u.Surname.ToUpper().Contains(name.ToUpper()));
}
}
}

“Simplicity is often one of the greatest strengths.”

—Erik Spoelstra

How To Use Repository With The Primary Constructors

program.cs: Firstly, add the “IRepository” to the Services with Transient() method on program.cs.

builder.Services.AddTransient<IUserRepository, UserRepository>();

UserController.cs: There are two different user methods, as shown below. Both use UserRepository. The IUserRepository is passed directly to the UserController class via the Primary Constructor, without being assigned to a separate variable.

using Microsoft.AspNetCore.Mvc;
using PrimaryConstructor.Models;
using PrimaryConstructor.Repository;

namespace PrimaryConstructor.Controllers
{
[ApiController]
[Route("[controller]")]
public class UserController(IUserRepository _repository) : ControllerBase
{

[HttpGet]
[Route("GetAllUsers")]
public List<User> GetAllUsers()
{
return _repository.GetUsers().ToList();
}
[HttpGet]
[Route("GetUserByName")]
public User? GetUserByName(string name)
{
return _repository.GetUserByName(name);
}
}
}

“When you remove layers, simplicity and speed happen.”

— Ginni Rometty

Conclusion:

If you are writing in .Net and especially using Dependency Injection in your projects, Primary Constructor will be very handy to you.

Constructors are crucial when classes and struct objects are first initialized, as they assign and modify the relevant properties and variables. Using parameters directly within the class, without a separate constructor, improves code readability and allows for quicker adjustments in future changes.

Improve Readability

Less Boilerplate Code

More Flexable Code

Good Bye

See you until the next article.

“If you have read so far, first of all, thank you for your patience and support. I welcome all of you to my blog for more!”

Source [Github]: https://github.com/borakasmer/PrimaryConstructor

Source: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/primary-constructors

--

--

Bora Kaşmer
Bora Kaşmer

Written by Bora Kaşmer

I have been coding since 1993. I am computer and civil engineer. Microsoft MVP. Software Architect(Cyber Security). https://www.linkedin.com/in/borakasmer/