What are C# Records

What are C# Records

Introduction

In C#, records are a type introduced in C# 9.0 as part of .NET 5. They are a reference type that provides a concise syntax to define immutable data structures.

Records are particularly useful for creating data-centric types whose primary purpose is to store data with little to no business logic.

Key Features of Records in C#

  1. Immutability: By default, records are immutable. Once their properties are set through the constructor, they cannot be changed. This makes records ideal for scenarios where data consistency and thread safety are crucial.
  2. Value-Based Equality: Unlike classes, records use value-based equality. Two record instances are considered equal if all their properties are identical. This is different from the reference-based equality used by classes.
  3. Concise Syntax: Records allow for a more concise syntax compared to classes. You can define properties directly in the record declaration, reducing the boilerplate code needed for property definitions.
  4. With Expressions: Records support with expressions, allowing you to create a new record instance by copying an existing instance and changing some properties in a more readable manner.
  5. Deconstructors: Records automatically support deconstruction, allowing you to decompose a record into variables.

Example of a Record in C#

Here’s an example illustrating how to define and use a record in C#:

public record Person(string FirstNamestring LastName); 

public static void Main(string[] args

    var person1new Person("John""Doe"); 
    var person2newPerson("John""Doe");
    
    // Value-based equality 
    Console.WriteLine(person1 == person2); // Output: True 
    
    // With expression 
    var person3 = person1 with { FirstName"Jane" };
    
    // Deconstruction 
    var (firstName, lastName) = person3; 
    Console.WriteLine(firstName);  // Output: Jane 
    Console.WriteLine(lastName);   // Output: Doe 
}

When to Use Records

  • Data Modeling: Records are excellent for data modelling, mainly when representing data structures that do not require modification after creation.
  • Functional Programming: They fit well in functional programming paradigms due to their immutability.
  • State Representation: Useful in scenarios like representing the state in a multi-threaded application where immutable data types help prevent race conditions.

The ‘with’ statement

The with expression in C# is a syntax feature used with records to create a new record instance based on an existing one, with some properties modified. It provides a concise and readable way to produce new immutable objects that differ only slightly from existing ones.

How Does the With Expression Work?

  • Immutability and Records: Since records are immutable by default, you cannot modify their properties once they are set. The with expression allows you to overcome this limitation by creating a new record while changing some of its properties.
  • Syntax: The with expression is used with an existing record instance. It follows the syntax: existingRecord with { Property = newValue }.

Example of the With Expression

Consider a record named Person:

public record Person(string Nameint Age);

Using the with expression, you can create a new Person instance based on an existing one but with a modified age:

var originalPersonnew Person("John"30); 
var olderPerson = originalPerson with { Age31 };

Here, olderPerson is a new instance with the Age property set to 31, while all other properties are copied from originalPerson.

Advantages of Using the With Expression

  1. Maintaining Immutability: It allows for modifying record instances while preserving their immutable nature.
  2. Code Readability and Clarity: The with expression provides a clear and concise way to indicate that a new object is a variation of an existing one.
  3. Reduced Boilerplate: It eliminates the need to manually copy properties to create slightly modified instances.

Considerations When Using the With Expression

  • Performance: Each with expression results in creating a new object, which could have performance implications in scenarios with heavy object creation and disposal.
  • Deep Copy vs. Shallow Copy: The with expression performs a shallow copy of the properties. If the record contains reference types, the original and the new records will reference the same objects.

Performance Considerations

Performance Considerations for Records

  1. Construction Overhead:
    • Creating a record involves initializing an immutable object. This process might be slightly more expensive than instantiating a regular class, especially if the record has numerous properties. However, this overhead is generally minimal and often negligible.
  2. Equality Comparisons:
    • Records implement value-based equality, which means comparing two record instances involves comparing each property value. This can be more performance-intensive than reference-based equality checks (typical with classes), particularly if the records have several properties or if the properties are complex types themselves.
    • For simple or small records, this performance cost is usually insignificant. However, developers should consider this aspect for large and complex records.
  3. Memory Footprint:
    • As records are immutable, changing a record’s data requires creating a new instance, leading to a higher memory churn than mutable objects that can be modified in place.
    • The with expression creates a new record instance while copying the data from an existing one. This is convenient for maintaining immutability but can impact performance if used excessively in performance-critical paths.
  4. Garbage Collection:
    • Increased object creation can lead to more frequent garbage collection. However, the impact of this depends heavily on the specific use case and how the records are utilized within an application.

Conclusion

Records in C# provide a more streamlined and efficient way to handle immutable data structures.

Their introduction reflects the shift towards functional programming patterns and offers developers a reliable and concise way to represent data.

Understanding and utilizing records can lead to cleaner, more maintainable, and thread-safe code in C# applications.

Stephen

Hi, my name is Stephen Finchett. I have been a software engineer for over 30 years and worked on complex, business critical, multi-user systems for all of my career. For the last 15 years, I have been concentrating on web based solutions using the Microsoft Stack including ASP.Net, C#, TypeScript, SQL Server and running everything at scale within Kubernetes.