C# > Functional Programming > Immutable Types > Records in C# 9+
Immutable Data with C# Records: A Functional Approach
This snippet demonstrates how to leverage C# 9+'s record types to create immutable data structures, a cornerstone of functional programming. Records offer a concise syntax for defining immutable types, promoting safer and more predictable code.
Basic Record Definition
This line defines a record named `Person` with three properties: `FirstName`, `LastName`, and `Age`. By default, records are immutable; their properties are initialized during creation and cannot be changed afterward.
public record Person(string FirstName, string LastName, int Age);
Creating and Using Records
This section showcases how to create instances of the `Person` record and access its properties. Crucially, it demonstrates the immutability of record properties. Attempting to directly modify `person1.Age` results in a compile-time error. The `with` expression is used to create a new `Person` record (`person2`) that is a copy of `person1` but with the `Age` property modified. The original object, `person1`, remains unchanged, ensuring immutability. Records provide structural equality by default, meaning two record objects are considered equal if all their properties have the same values, as shown in the comparison of `person1` and `person3`.
Person person1 = new("Alice", "Smith", 30);
Console.WriteLine($"Person: {person1.FirstName} {person1.LastName}, Age: {person1.Age}");
// Demonstrating Immutability (this will cause a compile-time error)
// person1.Age = 31; // Error: Init-only property or indexer 'Person.Age' can only be assigned in an object initializer, or on 'this' or 'base' in an instance constructor or init accessor. It cannot be assigned to after the object is constructed.
// Creating a modified copy using 'with' expression
Person person2 = person1 with { Age = 31 };
Console.WriteLine($"Original Person Age: {person1.Age}");
Console.WriteLine($"Modified Person Age: {person2.Age}");
//Value equality
Person person3 = new("Alice", "Smith", 30);
Console.WriteLine($"person1 equals person3? {person1 == person3}"); //output: true
Concepts Behind the Snippet
This snippet demonstrates immutability using C# records. Immutability means that once an object is created, its state cannot be changed. Functional programming principles strongly advocate for immutability because it simplifies reasoning about code and reduces the risk of side effects. Records in C# 9+ are specifically designed to support immutability by default, making them ideal for functional programming paradigms.
Real-Life Use Case
Imagine an application that processes financial transactions. Each transaction can be represented as an immutable record. Once a transaction is created and stored, it should never be modified. Any corrections or adjustments would be represented as new transactions linked to the original one. This immutability guarantees the integrity and auditability of the financial data.
Best Practices
Always strive to design your data structures as immutable whenever possible. Use records in C# 9+ to achieve immutability with minimal code. Avoid mutable state as much as you can to make your code more predictable and easier to test. Use the `with` expression to create modified copies of records instead of trying to mutate them directly.
Interview Tip
Be prepared to discuss the benefits of immutability and how records in C# 9+ support this principle. Explain how immutability contributes to code safety, thread safety, and easier debugging. Also, understand the trade-offs, such as the potential for increased memory usage due to creating new objects instead of modifying existing ones.
When to Use Records
Use records when you need immutable data structures that primarily hold data. They are particularly well-suited for representing domain objects, data transfer objects (DTOs), or any scenario where data integrity is crucial. If you need mutable data structures, consider using classes instead.
Memory Footprint
Creating a new record (even with the `with` expression) typically involves allocating new memory for the new object. While records provide a convenient syntax, creating many copies of large records could potentially impact performance and increase memory consumption. Carefully consider the trade-offs between immutability and memory usage, especially in performance-critical scenarios. However, the immutability can help in multi-threaded scenarios, reducing the need for locking, which can improve performance.
Alternatives
Before C# 9, you could achieve immutability using classes and carefully designing them to prevent state changes (e.g., using private setters and readonly fields). Libraries like `ImmutableCollections` offer immutable data structures. However, records provide a more concise and built-in way to create immutable types. Another alternative is using structs but they're value types which can have different behaviors when comparing to records.
Pros
Concise syntax for creating immutable types. Built-in value-based equality. Simplified data transfer and handling. Thread safety due to immutability. Easier to reason about code and debug. Improved code maintainability. Structural equality comparison.
Cons
Potential for increased memory usage if many copies of large records are created. Requires C# 9 or later. May not be suitable for scenarios where frequent mutation is required.
FAQ
-
What is immutability?
Immutability means that once an object is created, its state (i.e., the values of its properties) cannot be changed. Any operation that appears to modify an immutable object actually creates a new object with the desired changes. -
Why is immutability important in functional programming?
Immutability simplifies reasoning about code, reduces the risk of side effects, and makes code more predictable and easier to test. It is a cornerstone of functional programming paradigms. -
How do records in C# 9+ support immutability?
Records in C# 9+ are immutable by default. Their properties are typically initialized during creation and cannot be modified afterward. The `with` expression provides a convenient way to create modified copies of records without altering the original object. -
What is structural equality?
Structural equality means that two objects are considered equal if all their corresponding properties have the same values. Records in C# provide structural equality by default, making it easy to compare records for equality.