Exploring Improvements in Records with C# 13

C#.NET 9
20-12-2024Tim De Belderreading time: 3 minutes

C# has consistently evolved to help developers write cleaner, more expressive, and maintainable code. One of the standout features introduced in recent years has been records, a type specifically designed to simplify working with immutable data. With C# 13, records receive significant improvements, enhancing their usability and making them an even more powerful tool for developers. Let’s dive into these improvements and explore their impact.


A Quick Recap: What Are Records?

Records were introduced in C# 9 as a way to create immutable, value-based types. Unlike classes, which are reference types, records are intended for scenarios where equality is based on the values of the properties rather than object references.

Key Features of Records:

  • Value-based Equality: Two record instances are equal if their properties have the same values.
  • Built-in Immutability: Properties in records are readonly by default, promoting immutability.
  • Concise Syntax: Records allow concise declaration of data objects using positional parameters.

Example:

1public record Person(string FirstName, string LastName);
2
3var person1 = new Person("Alice", "Johnson");
4var person2 = new Person("Alice", "Johnson");
5
6Console.WriteLine(person1 == person2); // Output: True
7

What’s New for Records in C# 13?

C# 13 builds upon the foundation of records with several enhancements aimed at improving their flexibility and usability. Here are the most notable improvements:

1. Parameterless Record Constructors

C# 13 introduces support for parameterless constructors in records. Previously, records required parameters for initialization, which could be limiting in scenarios requiring default values.

Before C# 13:

1public record Product(string Name, decimal Price);
2
3var defaultProduct = new Product(); // Compilation error
4

With C# 13:

1public record Product
2{
3    public string Name { get; init; } = "Unknown";
4    public decimal Price { get; init; } = 0.0m;
5}
6
7var defaultProduct = new Product();
8Console.WriteLine(defaultProduct.Name); // Output: Unknown
9

2. Simplified Customization of Equality

While value-based equality has always been a cornerstone of records, developers now have greater flexibility to customize equality logic directly in the record declaration.

Example:

1public record Circle(double Radius)
2{
3    public override bool Equals(object? obj) =>
4        obj is Circle c && Math.Abs(c.Radius - Radius) < 0.01;
5
6    public override int GetHashCode() => Radius.GetHashCode();
7}
8
9var circle1 = new Circle(5.0);
10var circle2 = new Circle(5.01);
11
12Console.WriteLine(circle1 == circle2); // Output: True
13

3. Improved Performance with Large Records

C# 13 introduces under-the-hood optimizations to improve the performance of records, especially when working with large or deeply nested records. These changes reduce memory usage and enhance runtime efficiency.


Why These Changes Matter

The improvements in C# 13 further solidify records as a go-to choice for immutable data modeling. By addressing limitations such as the lack of parameterless constructors and providing greater flexibility in equality customization, these enhancements make records more adaptable to a wide range of scenarios.


When to Use Records

Consider using records in the following scenarios:

  • Immutable Data Models: Represent data that doesn’t change after initialization.
  • Value-Based Comparisons: Focus on comparing objects by their values, not their references.
  • Data Transfer Objects (DTOs): Use records for API contracts and data exchange.

Conclusion

C# 13 continues to refine records, making them an indispensable tool for modern .NET developers. With enhancements like parameterless constructors, customizable equality, and performance improvements, records are now more powerful and versatile than ever. As you adopt C# 13, consider leveraging these improvements to build cleaner, more maintainable applications.