C# tutorials > Modern C# Features > C# 6.0 and Later > What are record types in C# 9.0 and what are their key characteristics?

What are record types in C# 9.0 and what are their key characteristics?

C# 9.0 introduced record types, a new reference type that provides concise syntax for creating immutable data models. Records offer value-based equality, meaning two records are considered equal if their properties have the same values, regardless of their reference. This is a significant departure from classes, where equality is based on reference by default. Records also provide built-in support for non-destructive mutation using the with expression.

Defining a Record

This code defines a simple record called Person. It has three properties: FirstName, LastName, and Age. This concise syntax automatically generates properties, a constructor, a deconstructor (for pattern matching), and value-based equality members.

public record Person(string FirstName, string LastName, int Age);

Value-Based Equality

This code demonstrates value-based equality. Even though person1 and person2 are different instances in memory, they are considered equal because their properties have the same values. The == operator, in the context of records, performs a deep comparison of the properties.

Person person1 = new("John", "Doe", 30);
Person person2 = new("John", "Doe", 30);

Console.WriteLine(person1 == person2); // Output: True

Non-Destructive Mutation with 'with' Expression

The with expression allows you to create a new record instance based on an existing one, with some properties modified. In this example, person2 is created as a copy of person1, but with the Age property updated to 31. Crucially, person1 remains unchanged. This ensures immutability.

Person person1 = new("John", "Doe", 30);
Person person2 = person1 with { Age = 31 };

Console.WriteLine(person1);
Console.WriteLine(person2);

Concepts Behind the Snippet

  • Immutability: Records encourage immutability, which simplifies reasoning about code and makes it easier to write thread-safe applications.
  • Value-Based Equality: Records provide a convenient way to compare objects based on their content rather than their memory address.
  • Concise Syntax: Records reduce boilerplate code compared to traditional classes when dealing with data-centric objects.

Real-Life Use Case: Data Transfer Objects (DTOs)

Records are excellent for representing DTOs in APIs and microservices. Since DTOs primarily hold data, the value-based equality and concise syntax of records make them a natural fit. The immutability ensures data integrity as it moves between layers.

Best Practices

  • Favor records over classes when representing data-centric objects with value semantics.
  • Use the with expression for non-destructive updates to maintain immutability.
  • Consider using positional records for simple data structures to further reduce boilerplate.
  • Avoid using mutable properties in records unless absolutely necessary, as it undermines the benefits of immutability.

Interview Tip

Be prepared to discuss the differences between records and classes, particularly regarding equality and immutability. Understand the benefits of records in data-centric scenarios and how the with expression works. Mention the performance implications of value-based equality (potentially slower than reference equality for complex objects).

When to Use Records

Use records when:

  • You need value-based equality.
  • You want to represent immutable data.
  • You want to reduce boilerplate code for data-centric objects.
  • You are working with DTOs or other data transfer objects.

Memory Footprint

The memory footprint of a record is generally similar to that of a class with auto-implemented properties. There might be a slight overhead due to the generated equality and deconstruction methods, but it's usually negligible. However, excessive use of the with expression can lead to increased memory consumption if many new record instances are created without proper garbage collection.

Alternatives

Alternatives to records include:

  • Classes: Can be used for mutable data and reference-based equality. Requires more boilerplate code for implementing equality and copy constructors for immutability.
  • Structs: Value types suitable for small, immutable data structures. Value-based equality is automatic, but they are copied on assignment, which can impact performance for larger structs. Records are reference types, so they avoid copying overhead.
  • Tuples: Useful for grouping unnamed values. Less structured and less readable than records.

Pros

  • Concise syntax.
  • Automatic value-based equality.
  • Built-in support for non-destructive mutation (with expression).
  • Encourages immutability.
  • Reduces boilerplate code.

Cons

  • Value-based equality can be slower than reference equality for complex objects.
  • Immutability can make certain operations more complex if mutable state is required.
  • Might be unfamiliar to developers used to traditional classes.

FAQ

  • What's the difference between a record and a class in C#?

    The key differences are: records have value-based equality (equality is determined by the properties, not the reference), records are designed to be immutable (although they can be mutable), and records have more concise syntax. Classes have reference-based equality by default and are generally mutable.

  • Can I make a record mutable?

    Yes, you can make a record mutable by using set accessors on its properties. However, it's generally recommended to keep records immutable to leverage their benefits.

  • What is a positional record?

    A positional record is a record where the constructor parameters define the properties directly. For example: public record Person(string FirstName, string LastName); This is even more concise than the example in 'Defining a Record'.