Structs and Records

Overview

Structs and records are types in C# that provide different ways of modeling data. Structs are value types, while records are reference types with built-in structural equality. Understanding their use cases can help in choosing the right type for a given scenario.

Structs

Value Types

Structs are value types, meaning they are stored on the stack and copied by value. They are typically used for small, immutable data structures and have better performance for such scenarios compared to classes.

Syntax

Structs are defined using the struct keyword, followed by the struct name and its body enclosed in curly braces {}.

Example

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

Equality

Structs support value equality by default. This means two structs are considered equal if all their fields are equal.

Example

Point point1 = new Point { X = 5, Y = 10 };
Point point2 = new Point { X = 5, Y = 10 };

Console.WriteLine(point1 == point2); // True
point1.Equals(point2); // True

Mutability

Structs can be mutable, but it is recommended to use immutable structs to prevent unintended side effects. An immutable struct has readonly fields or properties and does not change after initialization.

public struct Point
{
    public int X { get; }   //look ma, no setter!
    public int Y { get; }

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

Records

Reference Types with Structural Equality

Records are reference types that provide value-based equality semantics. They are ideal for data models that do not require behavior (e.g. methods) and are mainly used to encapsulate immutable data.

Syntax

Records are defined using the record keyword, followed by the record name and its body enclosed in curly braces {}.

Example

public record Person(string Name, int Age);

With Expressions

The with expression allows you to create a new record based on an existing record, with modifications to some properties.

Example

var person1 = new Person("John Doe", 30);
var person2 = person1 with { Age = 31 };

Console.WriteLine(person1); // Person { Name = John Doe, Age = 30 }
Console.WriteLine(person2); // Person { Name = John Doe, Age = 31 }

Structural Equality

Records provide built-in value equality, which means two records are considered equal if all their properties are equal.

Example

var person1 = new Person("John Doe", 30);
var person2 = new Person("John Doe", 30);

Console.WriteLine(person1 == person2); // True
person1.Equals(person2); // True

Records vs. Classes vs. Structs

Structs

  • Performance: Best suited for small, immutable data types due to value semantics.
  • Use Cases: High-performance scenarios, geometric points, complex numbers.

Records

  • Data-Centric: Best for immutable data models that do not require behavior.
  • Use Cases: DTOs (data transfer objects), configuration objects, immutable data transfers.

Classes

  • Flexibility: Ideal for complex objects with behavior and mutable state.
  • Use Cases: Domain models, services, business logic.