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:

  1. Does the object represent a single concept or value?
  • Yes? Use a struct.
  • No? Use a class or record.
  1. Is the object immutable?
  • Yes? Consider a record or immutable struct.
  • No? Use a class.
  1. Is memory allocation or performance a concern?
  • Yes? Use a struct for lightweight, stack-allocated types.
  • No? Use a class or record.
  1. Do you need value-based equality?
  • Yes? Use a record or struct.
  • No? Use a class.

Quick Comparison

FeatureClassStructRecord
TypeReferenceValueReference
Stored OnHeapStack (mostly)Heap
ImmutabilityOptionalOptionalDefault (immutable)
EqualityReference-basedValue-basedValue-based
InheritanceYesNoLimited (sealed by default)
Use CaseBehavior-heavyLightweight dataImmutable 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.