.NET
structs
performance
memory optimization
programming tips

Why should a .NET struct be less than 16 bytes?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

The advice that a .NET struct should be under 16 bytes is a guideline, not a runtime rule. It exists because structs are value types, and once they get large enough, copying them around becomes expensive enough that many of the usual benefits of a struct start to disappear.

Why Struct Size Matters

A struct is copied by value unless you deliberately pass it by ref, in, or out. That means assignment, argument passing, return values, and collection operations may move the entire payload.

For a tiny value type, that cost is low:

csharp
1public readonly struct Point2D
2{
3    public int X { get; }
4    public int Y { get; }
5
6    public Point2D(int x, int y)
7    {
8        X = x;
9        Y = y;
10    }
11}

Point2D is only two int fields. Copying it is cheap, storing many of them in an array is cache-friendly, and its value semantics are natural.

Once a struct becomes much larger, every copy moves more data. That is when a reference type or a different API shape may become a better fit.

Why People Mention 16 Bytes Specifically

The 16-byte number is historical guidance tied to common CPU and JIT tradeoffs. Small structs often fit comfortably into registers or into cheap calling-convention paths, while larger ones are more likely to involve extra copying and stack traffic.

There is nothing magical about 16 itself. The CLR does not punish a 17-byte struct, and a 24-byte readonly struct can still be perfectly reasonable. The point is that, past a certain size, you should stop assuming that "struct equals faster" and start checking how the type is actually used.

A better reading of the rule is this:

  • use structs for small values
  • keep them simple
  • prefer immutability
  • avoid large by-value payloads unless you have measured the tradeoff

Immutability Matters More Than the Exact Byte Count

A tiny mutable struct can still be a bad design because value semantics make accidental copies easy to create. That leads to confusing bugs when someone edits a copy and expects the original to change.

csharp
1public struct Counter
2{
3    public int Value;
4
5    public void Increment()
6    {
7        Value++;
8    }
9}

If a Counter instance is copied out of a collection or property, calling Increment() may mutate only the copy. That is one reason readonly struct is usually a stronger recommendation than any hard size threshold.

Large Structs Are Not Automatically Wrong

Sometimes the data really is a value and really should stay together. For example, a math library may want a vector or matrix type with value semantics even when the type is larger than 16 bytes.

In those cases, the usual optimization is to keep the type immutable and avoid unnecessary copies at call sites:

csharp
1public readonly struct RectangleD
2{
3    public double X { get; }
4    public double Y { get; }
5    public double Width { get; }
6    public double Height { get; }
7
8    public RectangleD(double x, double y, double width, double height)
9    {
10        X = x;
11        Y = y;
12        Width = width;
13        Height = height;
14    }
15}
16
17public static double Area(in RectangleD rect)
18{
19    return rect.Width * rect.Height;
20}

Using in avoids copying the whole struct into the method while still preserving value semantics for callers.

When a Class Is the Better Choice

If the type is large, frequently mutated, or identity matters more than pure value equality, a class is usually easier to reason about. Classes move references instead of full payloads, so passing them around is cheaper when the object is large.

This is especially true when the type contains many fields, is often boxed, or is repeatedly passed through layered APIs. In those cases, insisting on a struct for theoretical performance reasons can actually make the code slower and harder to maintain.

Common Pitfalls

  • Treating 16 bytes as a strict runtime limit is incorrect; it is only a design heuristic.
  • Focusing on size while ignoring mutability misses the bigger source of struct bugs.
  • Assuming all structs live on the stack is wrong; a struct field inside a class lives wherever the containing object lives.
  • Making a large struct and then passing it everywhere by value can quietly introduce real copy overhead.
  • Using a struct that frequently boxes into object or non-generic interfaces throws away much of the expected performance gain.

Summary

  • The under 16 bytes advice is a performance guideline, not a CLR rule.
  • Small structs work well because by-value copies stay cheap.
  • 'readonly struct is usually more important than obeying an exact byte threshold.'
  • Larger structs can still be valid when value semantics are the right model.
  • If a struct is large, use APIs such as in carefully, or reconsider whether the type should be a class instead.

Course illustration
Course illustration

All Rights Reserved.