Each serves a specific purpose, and the choice depends on what you're building—immutability, performance, or how your type should behave.
Here's a breakdown of when to use which and why.
Classes
A class is the default choice for defining reference types in C#. If you’ve been writing C# for a while, you’ve probably been using classes without thinking twice about it.
When to Use a Class
- When Behavior Trumps Data: Classes are your go-to for defining objects with lots of methods or behavior beyond just holding data.
- When State Changes Over Time: Classes are flexible with mutable properties, allowing their state to change throughout their lifecycle.
- When You Want References: Classes are stored on the heap and behave like reference types—meaning multiple variables can point to the same object.
Example:
public class Vehicle
{
public string Make { get; set; }
public string Model { get; set; }
public int Year { get; set; }
public void Start() => Console.WriteLine($"{Make} {Model} is starting.");
}
What Happens in Memory?
- Reference type: A variable stores the reference to the object on the heap.
- Ideal for objects with a longer lifecycle or those shared between parts of your program.
Structs
A struct is a value type, often thought of as the lightweight sibling of a class. They're great when you don’t need the overhead of reference types.
When to Use a Struct
- Small, Simple, and Immutable: Structs work best for small objects that don’t change after creation.
- Performance Matters: Because structs are stored on the stack, they’re faster to allocate and deallocate compared to classes on the heap.
- You Want Copies, Not References: Assigning a struct copies the data instead of passing a reference.
Example:
public struct Point
{
public int X { get; }
public int Y { get; }
public Point(int x, int y)
{
X = x;
Y = y;
}
public double DistanceToOrigin() => Math.Sqrt(X * X + Y * Y);
}
What Happens in Memory?
- Value type: Stored on the stack (unless it's part of a heap-allocated object).
- Copied on Assignment: Each variable gets its own copy, avoiding unintended side effects.
Records
Records were introduced in C# 9, and they sit somewhere between classes and structs. They’re reference types (like classes) but are designed for immutable data with value-based equality.
When to Use a Record
- Data-Centric Scenarios: Records shine when the focus is on the data, not the behavior.
- Immutability Required: Records are immutable by default, so they’re great for data objects you don’t want to accidentally modify.
- Value-Based Equality: Records compare their values, not their references, making them perfect for cases like configuration or models.
Example:
public record Product(string Name, decimal Price);
Why Use a Record?
- Immutable Reference Type: Like classes, records are stored on the heap, but they behave more like structs in how equality is determined.
- Easy to Copy: Use the
with
keyword to create modified versions without changing the original.
Example in Action:
var product1 = new Product("Laptop", 999.99m);
var product2 = product1 with { Price = 899.99m };
Console.WriteLine(product1 == product2); // False, as their values differ.
Console.WriteLine(product1); // Output: Product { Name = Laptop, Price = 999.99 }
How to Choose
Here’s how to decide between these three:
- Does the object represent a single concept or value?
- Yes? Use a struct.
- No? Use a class or record.
- Is the object immutable?
- Yes? Consider a record or immutable struct.
- No? Use a class.
- Is memory allocation or performance a concern?
- Yes? Use a struct for lightweight, stack-allocated types.
- No? Use a class or record.
- Do you need value-based equality?
- Yes? Use a record or struct.
- No? Use a class.
Quick Comparison
Feature | Class | Struct | Record |
---|---|---|---|
Type | Reference | Value | Reference |
Stored On | Heap | Stack (mostly) | Heap |
Immutability | Optional | Optional | Default (immutable) |
Equality | Reference-based | Value-based | Value-based |
Inheritance | Yes | No | Limited (sealed by default) |
Use Case | Behavior-heavy | Lightweight data | Immutable data models |
Summary
Choose class when you need complex objects or mutable state. Go for struct when performance is key, and the type is simple. Use record when you want immutable, data-focused types with value-based equality.