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); // 100The same happens when passing a struct to a method: the method receives a copy.
Stack & heap
Struct vs class: copying values and references
Code
1var s1 = new PointStruct { X = 1 };2var s2 = s1;3s2.X = 99;45var c1 = new PointClass { X = 1 };6var c2 = c1;7c2.X = 99;
Stack
Main()
- PointStructs1{ X = 1 }
Heap
(empty)
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)); // TrueDefault 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).
| class | struct | |
|---|---|---|
| Kind | Reference type | Value type |
| Assignment copies | The reference | All the data |
Can be null | Yes | No (unless T?) |
| Inheritance | Yes | No |
| Typical use | Objects with identity and behavior | Small immutable values |
Try it
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").
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 pass1.Point is a struct. After `var a = new Point { X = 1 }; var b = a; b.X = 5;`, what is a.X?
2.Which of these is a struct in .NET?
3.When is a struct a good choice?
4.What does `readonly struct` guarantee?
To complete this lesson
- ○ Solve the exercise
- ○ Pass the quiz (3/4 correct)