Phase 2 of 30 · Topic 2.3

Boxing & Unboxing Mechanics & Heap Pressure Avoidance

1Concept

Boxing converts a value type to `object` or an interface by allocating an object box on the heap and copying the value into it. Unboxing extracts the value pointer. Frequent boxing in hot loops creates severe Gen 0 GC allocation pressure.

2Architecture Diagram

Boxing Operation:
[ int x = 100 (Stack) ] ──> Heap Allocate (16B Header + 4B Val + 4B Padding = 24B) ──> [ Boxed Object ]

Unboxing Operation:
[ Boxed Object (Heap) ] ──> Type check MethodTable ──> Copy 4B value back to Stack

3Code Example

C# 13 & .NET 9
using System;

public struct Metric : IComparable<Metric>
{
    public int Value;
    public int CompareTo(Metric other) => Value.CompareTo(other.Value);
}

public class BoxingDemo
{
    public static void Main()
    {
        Metric m1 = new Metric { Value = 50 };
        
        // Boxing occurs when casting to object or interface
        object boxed = m1; 
        IComparable<Metric> iface = m1; // Allocates heap box!

        // Generic constraint prevents boxing (JIT generates specialized struct code)
        int cmp = CompareGeneric(m1, new Metric { Value = 80 });
        Console.WriteLine($"Zero-allocation comparison result: {cmp}");
    }

    public static int CompareGeneric<T>(T a, T b) where T : IComparable<T>
    {
        return a.CompareTo(b); // Direct struct call, NO boxing!
    }
}

4Expected Output

Zero-allocation comparison result: -1

5Key Takeaways

  • Always use generic interfaces (e.g. `IEquatable<T>`, `IComparable<T>`) to prevent boxing.
  • Avoid `String.Format("{0}", intVal)` before C# 10; use string interpolation which compiles to `DefaultInterpolatedStringHandler`.
  • Use `in` or `ref readonly` parameters for large structs (>16 bytes) to prevent defensive copying.