C# tutorials > Modern C# Features > C# 6.0 and Later > What are record structs in C# 10 and how do they differ from record classes?
What are record structs in C# 10 and how do they differ from record classes?
C# 10 introduced record structs, expanding the record
concept, which was initially available only for classes, to value types (structs). Record structs, like record classes, offer concise syntax for creating immutable data-centric types, but with value-type semantics. This tutorial will explain record structs, highlight their differences from record classes, and provide examples to illustrate their usage.
Introduction to Record Structs
Record structs are value types (structs) that provide concise syntax for creating immutable or semi-immutable data structures. They automatically generate methods such as equality checks, ToString()
, and deconstructors based on the properties defined in the record. This reduces boilerplate code and improves readability. They offer a more efficient alternative to record classes when you need value-type semantics and immutability.
Basic Record Struct Declaration
This example demonstrates a simple record struct Point
with two properties, X
and Y
. The compiler automatically generates methods for equality comparison and ToString()
. Notice that despite being structs, equality comparison (==
) compares the values of the properties, just like record classes, not the references.
public record struct Point(double X, double Y);
public static class Example
{
public static void Main(string[] args)
{
Point p1 = new Point(10, 20);
Point p2 = new Point(10, 20);
Console.WriteLine(p1);
Console.WriteLine(p1 == p2); // Output: True
}
}
Differences Between Record Structs and Record Classes
The key difference lies in their type category: record structs are value types, while record classes are reference types. This distinction affects how they are stored in memory, how they are copied, and their behavior regarding nullability.
Point?
, i.e., nullable value type), whereas record classes can be null.
Mutability and the with
Expression
Like record classes, record structs support the with
expression for non-destructive mutation. This creates a new instance with specified properties changed. Even if the record struct is immutable, the with
expression allows you to easily create a new instance based on an existing one.
public record struct ImmutablePoint(double X, double Y);
public static class Example
{
public static void Main(string[] args)
{
ImmutablePoint p1 = new ImmutablePoint(10, 20);
ImmutablePoint p2 = p1 with { X = 30 }; // Creates a new instance with X updated
Console.WriteLine(p1);
Console.WriteLine(p2);
}
}
concepts behind the snippet
Value vs. Reference Types: Understanding the difference between value types (structs) and reference types (classes) is crucial. Value types are stored directly in memory where they are declared, while reference types store a reference to a memory location.
Immutability: Immutability means that once an object is created, its state cannot be changed. Record structs and classes encourage immutability.
Non-destructive Mutation: The with
expression allows creating new instances based on existing ones without modifying the original object.
Real-Life Use Case Section
Data Transfer Objects (DTOs): Record structs are excellent for representing DTOs that are passed between layers of an application. Since they are value types, they are copied when passed, ensuring that changes to the DTO in one layer do not affect other layers.
Mathematical Vectors: Representing mathematical vectors (e.g., 2D or 3D points) with record structs is beneficial because these are often immutable and require value semantics.
Configuration Objects: Small configuration settings or flags can be efficiently represented using record structs for quick access and immutability.
Best Practices
Choose Based on Semantics: If you need value-type semantics (copying, no nullability), use record structs. If you need reference-type semantics (sharing references, nullability), use record classes.
Embrace Immutability: Design record structs to be immutable whenever possible to avoid unexpected side effects.
Consider Size: For large data structures, a record class might be more efficient to avoid excessive copying overhead associated with structs.
Interview Tip
Be prepared to explain the key differences between record structs and record classes, focusing on value vs. reference type semantics and implications for memory management and behavior. Also, be ready to discuss immutability and the use of the with
expression.
When to use them
Use Record Structs when:
Avoid Record Structs when:
Memory footprint
Record structs generally have a smaller memory footprint than record classes if they are not boxed. Since record structs are value types, they are stored directly in memory (typically on the stack), avoiding the overhead of heap allocation and garbage collection. Record classes, being reference types, are stored on the heap, requiring additional memory for object headers and potentially leading to garbage collection overhead. However, if a record struct is boxed (e.g., when passed as an object
), it will be allocated on the heap, negating the memory footprint advantage. For small data structures, the difference is negligible, but for high-performance scenarios or resource-constrained environments, the memory footprint can be a significant consideration.
alternatives
Regular Structs: You can use regular structs to achieve value-type semantics. However, you would need to manually implement equality checking, ToString()
, and other utility methods.
Record Classes: If you need reference-type semantics or nullability, record classes are the natural alternative.
Immutable Collections: For more complex data structures, consider using immutable collections from the System.Collections.Immutable
namespace.
pros
Value-Type Semantics: Ensures data is copied, preventing unintended side effects.
Concise Syntax: Reduces boilerplate code with automatic generation of equality members and other methods.
Memory Efficiency: Can reduce memory footprint and garbage collection overhead compared to record classes when used appropriately.
Immutability: Encourages immutable data structures, improving code reliability.
cons
Copying Overhead: Can be inefficient for large data structures due to copying on assignment.
No Nullability: Cannot be null unless declared as a nullable value type (Point?
).
Boxing: Can be boxed (allocated on the heap) when used as an interface or object type, losing the memory efficiency.
FAQ
-
Can record structs inherit from other structs or classes?
No, structs (including record structs) cannot inherit from classes. They can implement interfaces. -
Are record structs always immutable?
No, record structs, like regular structs, can be mutable. However, it is best practice to design them as immutable to leverage the benefits of value-type semantics and avoid unintended side effects. -
When should I use a record struct over a regular struct?
Use a record struct when you want the concise syntax for generating equality members,ToString()
, and deconstructors automatically. Regular structs are suitable when you want more control over the implementation and don't need these features.