Welcome to today’s post.
In today’s post I will explain what Primary Constructors are and then show how to use them in .NET Core applications.
Before I describe primary constructors are and how they are used, in the first section I will give an overview of some of the most common constructors that we use in applications. I will also give an overview of how constructors are used within .NET Core dependency injection before I go into the justifications for using primary constructors within an application.
Explaining the Different Types of Class Constructors
We are familiar with class constructors that are used when we create an instance of the class within the application.
Class constructors take different forms:
- Instance constructors.
- Static constructors.
- Parameterless constructors.
- Primary constructors.
The first three forms are the most used types of class constructors.
Instance constructors are created by using the new() operator.
Parameterless Constructors
When using the built-in dependency injection framework within .NET Core or .NET, the class is instantiated with at least one declared instance constructor. An instance constructor that contains no parameters is called a parameterless constructor.
The most common area where parameterless constructors are used is when we use numeric value types, where we can declare the type and assign it to a value, or instantiate it with the new() operator.
In the case below, the type int is instantiated with no parameters, with the internal value defaulted to zero:
int x = new int();
Instance Constructors
Instance constructors are methods that are used to construct an instance of the class. They are declared inside the class declaration scope with a method that can contain none or more than one parameter.
An example is show below:
public class Book
{
public int _id;
public string _author;
public string _title;
public string _mediaType;
public Book() { }
public Book(int mediaType)
{
_mediaType = mediaType;
}
public Book(int id, string author, string title, string mediaType)
{
_id = id;
_author = author;
_title = title;
_mediaType = mediaType;
}
…
}
Where there are multiple instance constructors within a class, this is known as overloading the constructor.
We can also declare the second constructor method to use the first constructor method as shown:
public Book(int id, string author, string title, string mediaType): this(mediaType)
{
_id = id;
_author = author;
_title = title;
}
Static Constructors
Static constructors are a special class that is not instantiated and cannot be instantiated with the new() operator. They are declared with the static keyword. The methods and properties of a static type are accessible through the name of the static class. Because static classes are not instanced, all members and properties are static. Static classes and its members and properties are created on the application heap, whereas instanced classes are allocated on the stack.
The constructor for a static class is always parameterless.
An example of a static class is shown below:
public static class MediaUtility
{
Public static string GetMediaType(string mediaName)
{
if (mediaName == “CD”)
return “Digital”;
if (mediaName == “DVD”)
return “Digital”;
if (mediaName == “Book”)
return “Physical”;
if (mediaName == “Journal”)
return “Physical”;
return “Unknown”;
}
}
An example with a static constructor is shown below:
public static class MediaUtility
{
static readonly string _mediaName;
static MediaUtility(string mediaName)
{
_mediaName = mediaName;
}
public static string GetMediaType()
{
if (_mediaName == “CD”)
return “Digital”;
if (_mediaName == “DVD”)
return “Digital”;
if (_mediaName == “Book”)
return “Physical”;
If (_mediaName == “Journal”)
return “Physical”;
return “Unknown”;
}
}
Primary Constructors
The release of the .NET 8.0 (also known as .NET Core 8.0) framework in the Visual Studio 2022 release 17.7 included the latest version of C# version 12.0.
One of the features of C# 12.0 was the inclusion of Primary Constructors.
Now, Primary Constructors are an extension of C# classes that allows us to declare a class with parameters not only as instance constructors, but also in the class declaration.
Below is an example of using primary constructors in a class that is inherited from another class:
public class LoanReportViewModel(int bookId): LoanViewModel
{
…
}
Another example is using primary constructors to inject services into a class that is implemented from an interface:
public class LoanService(IBookService bookService, ApplicationDbContext db,
ILogger<LoanService> logger): ILoanService
{
…
}
In the sub-section on instance constructors, where I explained the use of overloaded constructor methods, with primary constructors, when there are multiple constructors used in the class, the additional constructor methods are required to call the primary constructor using the this() operator.
Unlike instance constructors, where all properties and fields are accessible using this operator within the class, with primary constructors, the constructor parameters cannot be treated as if they are members and so cannot use this operator.
In the sub-section on static constructors, where I explained that members and properties within the static class are stored in once in the application heap, with primary constructors, the primary constructor parameters are stored within the class only when the parameters are used within any of the class properties or methods.
In the next section, I will provide an overview of constructors used with dependency injection.
Constructors and Dependency Injection
In the previous section, I mentioned and gave an overview of instance constructors. With the .NET Core application framework, the inclusion of the dependency injection library allows dependencies of a class to be injected into that class without manually creating the instance using the new() operator.
The setup of dependency injection requires the creation of interfaces that define the behaviour of the classes we want to instance within the service collection. The classes are the implementation behind each interface. When the interface is declared within our application and is mapped to the class, the dependency injection framework implements an instance of the class from the interface.
Dependencies within a class are injected through use of a non-parameterless constructor, where the parameters within the constructor are interfaces that correspond to the dependent services of the class we are injecting.
An example of injecting services through an instance constructor is shown below:
public class BookService: IBookService
{
readonly ApplicationDbContext _db;
private readonly ILogger _logger;
public BookService(ApplicationDbContext db, ILogger<BookService> logger
{
_db = db;
_logger = logger;
}
…
}
In the next section, I will cover some important justifications why we should consider using primary constructors in application classes.
Reasons for using Primary Constructors in a Class
To Initialize Properties within a Class
When creating a class instance, when we define a parameterless constructor, we would set some class properties or fields to default values within the constructor.
When our constructor method is overloaded with parameters, the class properties or fields would be assigned the input parameter values or used to compute something that is used within the constructor method. An example shows how we set internal properties using an instance constructor method:
public class LoanReportViewModel: LoanViewModel
{
public int BookID { get; set; }
public string Title { get; set; }
public string Author { get; set; }
public class LoanReportViewModel(int bookId)
{
BookID = bookId
}
…
}
Assigning class properties using primary constructors is achieved in the example shown:
public class LoanReportViewModel(int bookId): LoanViewModel
{
new public int BookID { get; } = bookId;
public string Title { get; set; }
public string Author { get; set; }
}
We can see how using primary constructors makes the class plumbing code cleaner.
Removal of Dependency Injection Plumbing Code from Classes
With a class that uses injected dependencies, the instance constructor method’s code block is used to assign values to internal properties:
public class BookService : IBookService
{
readonly ApplicationDbContext _db;
private readonly ILogger _logger;
public BookService(ApplicationDbContext db, ILogger<BookService> logger
{
_db = db;
_logger = logger;
}
…
}
To assign values to the internal properties, we can also use primary constructors.
public class BookService(ApplicationDbContext db, ILogger<BookService> logger): IBookService
{
readonly ApplicationDbContext _db = db;
private readonly ILogger _logger = logger;
…
}
As we did for assigning properties, the use of primary constructors has made our code tidier.
Parameter Visibility in the Class
Primary constructor parameters are visible to the scope of the class definition, including properties and members including methods.
One of the inefficiencies of instance constructors is the assigning of internal properties or member fields before they can be used within the class. An efficiency of primary constructor parametrization is to allow the constructor parameters to be used by any properties or members including methods within the class immediately after the class is instanced.
In the example below, we can use the data context constructor parameter in a member method that retrieves a list of Books:
public class BookService(ApplicationDbContext db,
ILogger<BookService> logger): IBookService
{
readonly ApplicationDbContext _db = db;
private readonly ILogger _logger = logger;
…
public async Task<List<BookViewModel>> GetBooks()
{
List<BookViewModel> books = await db.Books.ToListAsync();
return books;
}
}
The above has been an overview of primary constructors, including how they compare with instance constructors and static constructors including advantages in using primary constructors.
In the next post, I will go into more details on how to use primary constructors in a .NET Core web application, and how to convert existing classes that use constructor instancing with dependency injection to use primary constructors.
That’s all for today’s post.
I hope that you have found this post useful and informative.

Andrew Halil is a blogger, author and software developer with expertise of many areas in the information technology industry including full-stack web and native cloud based development, test driven development and Devops.