Struct vs. Record vs. Class

Struct (struct)


  • Value Type - A struct is a value type, meaning it is stored on the stack (or inline within other types) and is passed by value.
  • Lightweight - Typically used for small, simple data structures that are meant to represent a single value (e.g., Point, Rectangle).
  • No Inheritance - structs do not support inheritance (other than implementing interfaces). However, they can have constructors, methods, fields, properties, and events.
  • Immutability - Often used for immutable data structures, though they can be mutable as well.
  • Performance - They generally have better performance for small objects because they avoid heap allocation and garbage collection overhead.


public struct Point
{
    public int X { get; set; }
    public int Y { get; set; }


    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }
}


Record (record)


  • Reference Type - A record is a reference type, stored on the heap and passed by reference.
  • Immutability - By default, records are immutable, though they can be made mutable. They are designed for data storage with a focus on immutability.
  • Value Equality - Records provide built-in value equality, meaning two record instances are considered equal if their properties have the same values, unlike classes where equality is based on reference unless overridden.
  • Primary Constructors - Records can define their properties directly in a concise manner using a primary constructor.
  • Inheritance - Records support inheritance like classes.


public record Point(int X, int Y);


Class (class)


  • Reference Type - A class is a reference type, stored on the heap, and passed by reference.
  • Mutable by Default - Unlike records, classes are typically mutable.
  • Inheritance - Classes support full inheritance, allowing for the creation of complex object hierarchies.
  • Encapsulation - Classes are designed for encapsulating data and behavior, making them ideal for business logic and complex data models.
  • Equality - By default, equality for classes is reference-based, but it can be overridden to implement value equality.


public class Point
{
    public int X { get; set; }
    public int Y { get; set; }


    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }


    public override bool Equals(object obj)
    {
        if (obj is Point other)
        {
            return X == other.X && Y == other.Y;
        }
        return false;
    }


    public override int GetHashCode() => (X, Y).GetHashCode();
}


When to Use Which:


  • Struct - Use when you have small, lightweight objects that represent a single value and where performance is critical (e.g., mathematical points, coordinates).
  • Record - Use when you need to create immutable data structures with built-in value equality, especially for DTOs (Data Transfer Objects) or entities in functional programming.
  • Class - Use when you need complex objects with encapsulated state and behavior, or when you require inheritance and polymorphism.