Phase 5 of 30 · Topic 5.5

Defensive Struct Copies & `readonly struct` Invariants

1Concept

Calling methods or properties on non-readonly structs passed with `in` causes the C# compiler to create a defensive copy of the struct in memory before the call to prevent possible mutations.

2Architecture Diagram

Non-Readonly Struct with `in`:
Method Call ──> Compiler creates hidden temporary stack copy (Memory Waste!)

`readonly struct` with `in`:
Method Call ──> Direct pointer dereference with ZERO defensive copies!

3Code Example

C# 13 & .NET 9
using System;

// Immutable struct guarantees zero defensive copying
public readonly struct Vector3D
{
    public readonly double X, Y, Z;

    public Vector3D(double x, double y, double z) => (X, Y, Z) = (x, y, z);

    public double MagnitudeSquared => X * X + Y * Y + Z * Z;
}

public class DefensiveCopyDemo
{
    public static double CalculateDistance(in Vector3D v)
    {
        // Zero defensive copy because struct is declared 'readonly struct'
        return v.MagnitudeSquared;
    }

    public static void Main()
    {
        var vec = new Vector3D(3.0, 4.0, 5.0);
        Console.WriteLine($"Vector Magnitude Squared: {CalculateDistance(in vec)}");
    }
}

4Expected Output

Vector Magnitude Squared: 50

5Key Takeaways

  • Always mark immutable structs as `readonly struct`.
  • Never pass mutable structs with `in` parameter modifier.
  • Mark individual struct members as `readonly` if the entire struct cannot be readonly.