When to use record vs class vs struct
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Choosing the appropriate data structure is crucial in C# programming to ensure optimal performance, code clarity, and maintainability. The three common data structures you'll encounter are records, classes, and structs. Each has its own specific use cases, benefits, and limitations. This guide will explore when to use each structure, supported by technical explanations and examples.
Classes
Overview
Classes are reference types and a fundamental concept in object-oriented programming. They are used to model real-world entities and encapsulate data and behavior related to those entities.
Characteristics
- Reference Type: Classes are always allocated on the heap.
- Mutable by Default: Instances of classes can be modified unless marked otherwise.
- Inheritance: Classes support inheritance, allowing the creation of subclasses.
- Polymorphism: Through interfaces or abstract classes, classes can participate in polymorphic behavior.
- Garbage Collection: Since they are stored on the heap, garbage collection is responsible for memory management.
Usage
- Encapsulating Complex Behavior: When your entity requires complex logic or behavior beyond just state representation.
- Long-lived Objects: For objects that persist over the application's lifetime.
- Inheritance and Polymorphism: When you need to leverage polymorphic behavior or inheritance.
Example
Structs
Overview
Structs are value types and are typically used for data structures that contain primarily data with little or no behavior.
Characteristics
- Value Type: Stored on the stack, which can provide performance benefits for small and frequently accessed objects.
- Immutability: Generally used for immutable objects, although this is not enforced by the language.
- No Inheritance: Cannot inherit from other structs or classes, but can implement interfaces.
Usage
- Small Data Structures: For grouping small amounts of data.
- Performance-Critical Applications: When the overhead of heap allocation is a concern.
- Immutable Entities: When defining simple, immutable data containers.
Example
Records
Overview
Records, introduced in C# 9.0, provide a concise syntax for creating immutable reference types, typically used to model immutable data.
Characteristics
- Immutable by Default: Designed to represent immutable data. The
withkeyword is often used to create modified copies. - Value Equality: Records implement value-based equality, comparing data rather than object reference.
- Reference Type: Like classes, records are reference types and stored on the heap.
Usage
- Data Transfer Objects (DTOs): For representing data intended to be passed along different layers or boundaries.
- Immutability: When you need immutable data structures with concise syntax.
- Automatic Synthesis: Auto-implemented members and constructors make records succinct and expressive.
Example
Additional Details
Comparing Mutability and Performance
- Mutability: Classes are mutable, whereas structs are typically used immutably, and records are immutable by default.
- Performance: Structs offer stack allocation benefits for small data, whereas classes and records involve heap allocation and garbage collection.
Considerations in Multithreading
- Thread Safety: Immutable types (records) offer inherent thread safety benefits.
- Synchronization: Mutable classes may require explicit synchronization mechanisms in concurrent environments.
Summary Table
| Feature/Aspect | Class | Struct | Record |
| Type | Reference | Value | Reference |
| Allocation | Heap | Stack | Heap |
| Default Mutability | Mutable | Typically immutable | Immutable |
| Inheritance | Supports | Does not support | Supports (since C# 10, after using record class) |
| Equality | Reference | Value | Value |
| Use Cases | Encapsulation, complex entities with behavior, polymorphism | Small data structures immutability, performance-critical | Immutable data transfer, concise syntax |
Choosing the right construct—record, class, or struct—depends on the specific requirements of your application, considering factors such as mutability, performance, complexity, and usage pattern. Understanding the nuances of each helps in building efficient and maintainable code.

