.NET Academy

Structs and value semantics

Use structs for small values that are copied rather than shared.

9 min read

You will learn

  • Explain how structs differ from classes
  • Predict what happens when a struct is copied
  • Use readonly structs and record structs
  • Decide when a struct is appropriate

Two kinds of types

Every type in C# is either a reference type or a value type. You've been using both all along:

  • Reference types — classes, records, arrays, string. A variable holds a reference to an object living elsewhere; copying the variable copies the reference.
  • Value types — int, double, bool, char, decimal, DateTime… and any struct you declare. A variable holds the value itself; copying the variable copies the data.

Declaring a struct

A struct looks almost exactly like a class:

struct Point
{
    public int X;
    public int Y;
 
    public Point(int x, int y) { X = x; Y = y; }
 
    public double DistanceFromOrigin() => Math.Sqrt(X * X + Y * Y);
}

Copy semantics

The difference shows up when you assign or pass it:

// With a struct
var a = new Point(1, 2);
var b = a;          // copies X and Y
b.X = 100;
Console.WriteLine(a.X);   // 1 — a is untouched
 
// With a class
var c = new PointClass(1, 2);
var d = c;          // copies the reference — same object
d.X = 100;
Console.WriteLine(c.X);   // 100

The same happens when passing a struct to a method: the method receives a copy.

Interactive

Stack & heap

Struct vs class: copying values and references

1/6

Code

1var s1 = new PointStruct { X = 1 };
2var s2 = s1;
3s2.X = 99;
4
5var c1 = new PointClass { X = 1 };
6var c2 = c1;
7c2.X = 99;

Stack

Main()

  • PointStructs1{ X = 1 }

Heap

(empty)

A struct is a value type: the whole point lives inside the variable, right in the stack frame.

Mutable structs are a trap

Because structs are copied so often, modifying one easily changes a temporary copy instead of the value you meant. The usual rule: make structs immutable.

readonly structs

readonly struct Temperature
{
    public double Celsius { get; }
    public Temperature(double celsius) => Celsius = celsius;
 
    public double Fahrenheit => Celsius * 9 / 5 + 32;
    public Temperature Warmer(double by) => new(Celsius + by);   // return a new value instead of mutating
}

readonly makes the compiler enforce immutability — every field must be read-only.

record structs

Just as record gives classes value equality and nice printing, record struct gives the same features to structs:

readonly record struct Rgb(byte R, byte G, byte B);
 
var orange = new Rgb(255, 128, 0);
Console.WriteLine(orange);                        // Rgb { R = 255, G = 128, B = 0 }
Console.WriteLine(orange == new Rgb(255, 128, 0)); // True

Default values

Every struct has a default value where all fields are zero, false or null. default(Point) and new Point() (without a custom parameterless constructor) both give (0, 0). A struct variable can never be null — unless you make it nullable, like Point? (Track 3).

Struct or class?

Choose a struct when the type:

  • represents a single small value (like a number, a coordinate, a color, a time span),
  • is small — roughly 16 bytes or less,
  • is immutable,
  • doesn't need inheritance (structs can implement interfaces, but can't inherit).

Otherwise — and when in doubt — use a class (or a record).

classstruct
KindReference typeValue type
Assignment copiesThe referenceAll the data
Can be nullYesNo (unless T?)
InheritanceYesNo
Typical useObjects with identity and behaviorSmall immutable values

Try it

Program.cs
Loading editor…

C# runtime not loaded

Key takeaways

  • Structs are value types: assignment and parameter passing copy the data.
  • Classes are reference types: copies share the same object.
  • Keep structs small and immutable (readonly struct, readonly record struct).
  • Default to classes; use structs for tiny value-like types.

Exercise

Two tasks. First, Point3 is a class, so copies share data — change it so that copying a Point3 creates an independent value. Second, implement Color.ToHex, which formats the color as "#RRGGBB" in uppercase hexadecimal (255, 128, 0 → "#FF8000").

Program.cs
Loading editor…

C# runtime not loaded

Hint 1

Changing one keyword turns Point3 into a value type.

Hint 2

The format specifier X2 prints a number as two uppercase hex digits: $"{R:X2}".

Show solution
var a = new Point3 { X = 1 };
var b = a;
b.X = 99;
Console.WriteLine($"a.X = {a.X}, b.X = {b.X}");
Console.WriteLine(new Color(255, 128, 0).ToHex());

struct Point3
{
    public int X;
    public int Y;
    public int Z;
}

record struct Color(byte R, byte G, byte B)
{
    public string ToHex() => $"#{R:X2}{G:X2}{B:X2}";
}

Quiz

4 questions · 3 correct to pass
  1. 1.Point is a struct. After `var a = new Point { X = 1 }; var b = a; b.X = 5;`, what is a.X?

  2. 2.Which of these is a struct in .NET?

  3. 3.When is a struct a good choice?

  4. 4.What does `readonly struct` guarantee?

To complete this lesson

  • ○ Solve the exercise
  • ○ Pass the quiz (3/4 correct)