Back to: C# New Features Tutorials
Primary Constructors for Records in C#
In this article, I will discuss Primary Constructors for Records in C# with Examples. Please read our previous article discussing Overload Resolution Priority Attributes in C# with Examples. In C# 13, Primary Constructors for records allow us to define constructor parameters directly in the record declaration, making the creation of records even more concise and easier to manage. This simplifies code and reduces the need for boilerplate code, especially in scenarios where records are used extensively for data modelling.
What Are Records in C#?
A record in C# is a reference type that provides built-in support for value-based equality. Unlike classes, where equality is determined by reference, records compare two instances by the values of their properties. Records are commonly used to represent immutable data structures, making them ideal for scenarios like DTOs (Data Transfer Objects), models, and immutable collections.
In C# 9, records were introduced with primary property declarations, allowing you to define properties in a compact form. However, C# 13 expands the power of records by introducing primary constructors, making it easier to initialize records directly in their declaration.
Before C# 13: Record Constructor and Initialization
Before C# 13, when defining a record, you would typically define a constructor either explicitly or implicitly. However, the constructor parameters still needed to be declared separately inside the record class, and there was no concise way to declare them directly in the record definition.
public record Person { public string FirstName { get; init; } public string LastName { get; init; } public int Age { get; init; } public Person(string firstName, string lastName, int age) { FirstName = firstName; LastName = lastName; Age = age; } }
What Are Primary Constructors for Records in C# 13?
In C# 13, you can define constructor parameters directly in the record declaration using the primary constructor syntax. This allows you to declare and initialize properties in a single statement, making the code more readable and reducing unnecessary boilerplate. The following is the syntax:
public record RecordName(Type ParameterName);
You can directly define properties in the constructor by including them in the primary constructor. The compiler automatically creates readonly properties from the constructor parameters, simplifying the code and improving clarity.
Key Features of Primary Constructors for Records:
- Conciseness: You can define a record with properties directly in the constructor, avoiding the need for separate property declarations.
- Built-in Immutable Properties: The properties are automatically immutable (readonly) unless you explicitly specify them as mutable.
- Improved Syntax: With primary constructors, you can define and initialize records in a more compact, readable, and intuitive way.
Example: Primary Constructor for Records in C# 13
Using a primary constructor for a Person record makes it more concise. The following example code is self-explained, so please read the comment lines for a better understanding:
namespace CSharp13NewFeatures { // Define a record using a primary constructor public record Person(string FirstName, string LastName, int Age); public class Program { static void Main() { // Create an instance of the Person record var person = new Person("John", "Doe", 30); // Access the properties of the Person record Console.WriteLine($"Name: {person.FirstName} {person.LastName}"); Console.WriteLine($"Age: {person.Age}"); } } }
Code Explanation:
- Person Record: The Person record uses a primary constructor where FirstName, LastName, and Age are passed directly in the constructor. The compiler automatically creates readonly properties from these parameters.
- Creating an Instance: In the Main method, an instance of the Person record is created, and we can access the properties directly (like FirstName, LastName, and Age).
Output:
Example with Additional Methods and Deconstruction
Primary constructors can also be combined with methods and deconstruction, making the record even more powerful.
namespace CSharp13NewFeatures { // Define a record with a primary constructor public record Employee(string Name, int Age, string Position) { // Method to display employee info public void DisplayEmployeeInfo() { Console.WriteLine($"Employee: {Name}, Age: {Age}, Position: {Position}"); } } public class Program { static void Main() { // Create an instance of the Employee record var employee = new Employee("Jane Smith", 28, "Software Developer"); // Accessing properties Console.WriteLine($"Employee Name: {employee.Name}"); Console.WriteLine($"Position: {employee.Position}"); // Display Employee Info using method in the record employee.DisplayEmployeeInfo(); // Deconstruction example var (name, age, position) = employee; Console.WriteLine($"Deconstructed - Name: {name}, Age: {age}, Position: {position}"); } } }
Code Explanation:
- Method: The Employee record defines a method, DisplayEmployeeInfo, to display the employee details.
- Deconstruction: You can deconstruct the record using the deconstruct keyword to unpack its properties into individual variables.
- Conciseness: The primary constructor allows the properties to be defined directly, making the code concise.
Output:
Benefits of Primary Constructors for Records
- Reduced Boilerplate Code: Primary constructors help you avoid creating explicit constructors and manually assigning property values. The compiler handles this automatically, making the code more concise and less error-prone.
- Improved Readability: By combining property declarations and constructor initialization in one place, the record becomes easier to read and maintain, especially when working with data models or DTOs.
- Built-In Immutability: When you define properties in a record’s primary constructor, they are automatically immutable (i.e., readonly), promoting safer and more predictable code.
- Ease of Use: Developers can now declare properties and initialize them directly in the record declaration, streamlining object creation and usage.
- Deconstruction and Pattern Matching: Primary constructors are compatible with deconstruction and pattern matching, improving flexibility and usability, especially in scenarios like object destructuring or pattern-based operations.
Real-Time Use Case: E-Commerce System
In an e-commerce application, you might have a Product record with properties such as Name, Price, and Category. With primary constructors, you can quickly and efficiently create product objects while ensuring their immutable properties.
namespace ECommerceApp { // Product record with a primary constructor public record Product(string Name, decimal Price, string Category) { public string GetProductDescription() => $"{Name} ({Category}) - {Price:C}"; } class Program { static void Main() { // Create a product with primary constructor var product = new Product("Laptop", 999.99m, "Electronics"); // Display product description using method in the record Console.WriteLine(product.GetProductDescription()); } } }
Code Explanation:
- Product Record: A record for representing products with properties like Name, Price, and Category.
- Primary Constructor: These properties are initialized directly in the record’s primary constructor.
- Method: The GetProductDescription method gives a simple description of the product.
The primary constructor feature in C# 13 makes defining records more concise and expressive. By allowing you to define properties directly in the constructor, C# 13 reduces boilerplate code, improves readability, and ensures immutability by default. This feature is especially useful for DTOs, data models, and other scenarios where you need compact, clear, and immutable data representations.
In the next article, I will discuss Default Interface Implementations for Static Members in C# with Examples. In this article, I explain Primary Constructors for Records in C# with Examples. I would like your feedback. Please post your feedback, questions, or comments about this article.