Question
In C#, when should you use a struct instead of a class?
My current mental model is that a struct is useful when an item is mainly a small group of values that logically belong together.
I also came across these common guidelines:
- A
structshould represent a single value. - A
structshould have a memory footprint smaller than 16 bytes. - A
structshould not be changed after creation.
Are these rules reliable? More importantly, what does a struct mean semantically in C#?
Short Answer
By the end of this page, you will understand the difference between struct and class in C#, especially the idea of value semantics versus reference semantics. You will learn why structs are usually best for small, immutable values like coordinates, dates, or colors, when classes are a better fit, what the common guidelines really mean, and how to avoid common mistakes such as accidental copying and unexpected mutations.
Concept
In C#, struct and class can both group related data and behavior, but they represent different kinds of meaning.
A class is a reference type:
- Variables store a reference to an object.
- Multiple variables can refer to the same instance.
- Changes through one reference are visible through others.
- Classes are a natural fit for entities with identity, lifecycle, and shared mutable state.
A struct is a value type:
- Variables store the actual value directly.
- Assigning a struct usually copies the value.
- Each copy is independent.
- Structs are best for small data that conceptually behaves like a single value.
What a struct means semantically
The most important idea is this:
- Use a struct when the thing is defined by its value.
- Use a class when the thing is defined by its identity.
For example:
- A
PointwithX = 10andY = 20is usually just a value. If two points have the same coordinates, they are effectively the same value. - A
Customeris usually an entity with identity. Two customers may have the same name and age but still be different customers.
Why the common rules exist
These guidelines are not hard language rules, but they are useful design advice.
1. "A struct should represent a single value"
This is mostly good advice.
A struct may contain multiple fields, but together they should form one conceptual value.
Examples:
DateTimeTimeSpanGuidPointRectangledecimal
A rectangle has multiple numbers, but it still represents one value: a rectangle.
2. "A struct should have a memory footprint less than 16 bytes"
This is a guideline, not an absolute rule.
Because structs are copied by value, very large structs can become inefficient. A small struct is usually cheap to copy. A large struct can increase memory traffic and reduce performance.
The 16-byte recommendation comes from practical performance advice, but it is not a strict compiler rule. You may still use larger structs in some cases, but you should do so carefully.
3. "A struct should not be changed after creation"
This is very strong advice and usually worth following.
Mutable structs can be confusing because copies happen implicitly. You may think you are modifying one value, but you are actually modifying a copy.
That is why immutable structs are much safer and easier to reason about.
Why this matters in real programming
Choosing between struct and class affects:
- correctness
- performance
- memory behavior
- API design
- ease of understanding
If you use a struct when identity matters, copying can create bugs. If you use a class for tiny value-like objects, you may create unnecessary heap allocations and more complex semantics.
A good rule of thumb:
- Choose
structfor small, immutable, value-like data. - Choose
classfor objects with identity, shared state, inheritance needs, or larger/more complex data.
Mental Model
Think of a struct like a number written on paper.
If you write 42 on one paper and copy it to another paper, you now have two separate copies of the same value. Changing one paper does not change the other.
That is how a struct behaves.
A class is more like a house address.
If two people both write down the same address, they are both referring to the same house. If someone paints that house blue, everyone referring to that address now sees the blue house.
That is how a class behaves.
So the question is:
- Is this thing more like a value you copy around?
- Or more like an object that multiple parts of the program refer to?
If it is a value, use a struct.
If it is an object with identity, use a class.
Syntax and Examples
Basic syntax
public struct Point
{
public int X { get; }
public int Y { get; }
public Point(int x, int y)
{
X = x;
Y = y;
}
}
public class Customer
{
public string Name { get; set; }
public Customer(string name)
{
Name = name;
}
}
Example: struct copies by value
Point p1 = new Point(10, 20);
Point p2 = p1;
Console.WriteLine(p1.X); // 10
Console.WriteLine(p2.X); // 10
p2 = p1; copies the value. After that, p1 and p2 are separate values.
Example: class copies the reference
Customer c1 = new Customer("Alice");
Customer c2 = c1;
c2.Name = "Bob";
Console.WriteLine(c1.Name); // Bob
Console.WriteLine(c2.Name); // Bob
c2 = c1; does not create a new customer object. It copies the reference, so both variables point to the same instance.
A good struct example
public readonly struct Size
{
public int Width { get; }
public int Height { get; }
public Size(int width, int height)
{
Width = width;
Height = height;
}
public override string ToString() => $"{Width} x {Height}";
}
This works well as a struct because:
- it represents one value
- it is small
- it is immutable
- it has no identity beyond its contents
A better fit for class
public class Order
{
public int Id { get; }
public List<string> Items { get; } = new();
public Order(int id)
{
Id = id;
}
}
This should be a class because:
- an order has identity
- it may change over time
- it contains mutable state
- multiple parts of a program may need to work with the same order instance
Step by Step Execution
Consider this example:
public struct Point
{
public int X;
public int Y;
}
Point p1 = new Point { X = 1, Y = 2 };
Point p2 = p1;
p2.X = 99;
Console.WriteLine($"p1: {p1.X}, {p1.Y}");
Console.WriteLine($"p2: {p2.X}, {p2.Y}");
Step by step
p1is created with valuesX = 1andY = 2.p2 = p1;copies the entire struct value.- Now
p1andp2contain the same data, but they are separate copies. p2.X = 99;changes onlyp2.p1stays unchanged.
Output:
p1: 1, 2
p2: 99, 2
Now compare that with a class:
public class PointClass
{
public int X;
public int Y;
}
PointClass p1 = new PointClass { X = 1, Y = 2 };
PointClass p2 = p1;
p2.X = 99;
Console.WriteLine($"p1: {p1.X}, {p1.Y}");
Console.WriteLine($"p2: {p2.X}, {p2.Y}");
Step by step
p1refers to one object in memory.p2 = p1;copies only the reference.- Both
p1andp2point to the same object. p2.X = 99;changes that shared object.- Reading through
p1shows the same change.
Output:
p1: 99, 2
p2: 99, 2
This is the core semantic difference.
Real World Use Cases
Good uses for struct
Coordinates and geometry
public readonly struct Point
{
public int X { get; }
public int Y { get; }
public Point(int x, int y)
{
X = x;
Y = y;
}
}
Used in:
- graphics
- game development
- UI layout
- mapping tools
Measurements and units
Examples:
- distance
- temperature
- money-like simple value wrappers
- duration
These are naturally value-like and often immutable.
Small configuration values
A small group of settings that behaves as a single value can work well as a struct.
Interop and low-level data
Structs are common when working with:
- binary file formats
- memory layouts
- platform interop
- high-performance code
Better uses for class
Domain entities
Examples:
UserOrderProductInvoice
These usually have identity and lifecycle.
Services and managers
Examples:
EmailServiceCacheManagerOrderProcessor
These represent behavior and often shared state.
Large or mutable objects
If the data is large or frequently changed, a class is often easier and safer.
Real Codebase Usage
In real C# projects, developers often use struct carefully and for specific reasons.
Common patterns with structs
Immutable value objects
A very common pattern is a readonly struct for small value objects.
public readonly struct Temperature
{
public double Celsius { get; }
public Temperature(double celsius)
{
Celsius = celsius;
}
}
This avoids accidental mutation and makes copying safe.
Representing coordinates, ranges, and identifiers
Developers often use structs for:
- coordinates
- dimensions
- ranges
- lightweight wrappers around primitive values
Equality by contents
Since structs are value-like, developers often expect equality to be based on contained values.
For example, two points with the same coordinates should usually be considered equal.
Common patterns with classes
Shared mutable state
If multiple parts of the app need to observe and modify the same object, a class is usually the right choice.
Validation and lifecycle logic
Classes often hold more complex rules, such as:
- creation rules
- state transitions
- persistence behavior
- event handling
Guard clauses and validation
Classes and structs both use validation, but classes more often model objects that must remain valid over time.
public class BankAccount
{
public decimal Balance { get; private set; }
public void Withdraw(decimal amount)
{
if (amount <= 0)
throw new ArgumentException("Amount must be positive.");
if (amount > Balance)
throw new InvalidOperationException("Insufficient funds.");
Balance -= amount;
}
}
This kind of stateful behavior is usually a better fit for a class.
Practical rule in codebases
Many teams default to class unless they have a clear reason to use struct, because structs introduce copy semantics that must be understood well. When teams do use structs, they usually keep them:
- small
- immutable
- value-like
- easy to compare and reason about
Common Mistakes
1. Making structs mutable
This is one of the biggest mistakes.
Broken example:
public struct Counter
{
public int Value;
}
Counter c1 = new Counter { Value = 5 };
Counter c2 = c1;
c2.Value++;
Console.WriteLine(c1.Value); // 5
Console.WriteLine(c2.Value); // 6
A beginner may expect c1 to change too, but it does not because c2 is a copy.
Avoid it
Prefer immutable structs:
public readonly struct Counter
{
public int Value { get; }
public Counter(int value)
{
Value = value;
}
}
2. Using structs for large data
Broken idea:
public struct HugeData
{
public long A, B, C, D, E, F, G, H;
}
This may be copied many times, which can hurt performance.
Avoid it
Use a class if the data is large or frequently passed around.
3. Using struct for objects with identity
Broken modeling example:
public struct User
{
public int Id;
public string Name;
}
A user usually has identity and lifecycle. Copying users by value is often not what you want.
Avoid it
Use a class for domain entities.
4. Assuming structs are always faster
This is not always true.
Structs avoid some heap allocations, but copying large structs can be expensive. Performance depends on usage.
Avoid it
Choose based on semantics first, then measure if performance matters.
5. Forgetting that a struct can contain reference-type fields
A struct is a value type, but its fields may still refer to objects.
public struct Wrapper
{
public string Text;
}
Copying Wrapper copies the string reference, not a deep copy of the string object. In the case of string, this is safe because strings are immutable, but with mutable reference-type fields this can be surprising.
Avoid it
Be careful when a struct contains mutable reference types like List<T>.
Comparisons
| Feature | struct | class |
|---|---|---|
| Type category | Value type | Reference type |
| Assignment | Copies the value | Copies the reference |
| Identity | Usually no separate identity | Usually has identity |
| Mutation | Best kept immutable | Often mutable |
| Typical size | Small | Any size |
| Default use case | Coordinates, dates, small value objects | Entities, services, complex state |
| Heap allocation | Often avoided in simple cases | Usually allocated as objects |
| Inheritance | Cannot inherit from another struct/class | Supports inheritance |
struct vs record class vs record struct
| Type | Best for |
|---|---|
class | Reference-based objects with identity or mutable shared state |
struct | Small value-like types, often immutable |
record class | Reference types with value-based equality semantics |
record struct | Value types with concise syntax and value-like behavior |
Simple decision guide
-
Use
structif the type is:- small
- immutable
- value-like
- not identity-based
-
Use
classif the type is:- large
- mutable
- shared across code
- identity-based
- behavior-rich or lifecycle-driven
Cheat Sheet
Quick rule
Use a struct for small, immutable, value-like data.
Use a class for identity-based, shared, mutable, or larger objects.
Struct signs
- Represents one conceptual value
- Small in size
- Safe to copy
- Prefer immutable
- Examples: point, size, date range, amount
Class signs
- Has identity
- Shared by multiple parts of code
- Changes over time
- Larger or more complex state
- Examples: user, order, service, repository
Key syntax
public readonly struct Point
{
public int X { get; }
public int Y { get; }
public Point(int x, int y)
{
X = x;
Y = y;
}
}
public class User
{
public string Name { get; set; }
}
Important rules of thumb
- Struct assignment usually copies the whole value.
- Class assignment copies a reference.
- Avoid mutable structs.
- Do not choose
structonly because you think it is faster. - The "under 16 bytes" rule is a guideline, not a hard requirement.
- Semantics come first; performance comes second.
Edge case reminder
A struct can still contain reference-type fields. That does not make it a deep copy automatically.
FAQ
Is a struct always stored on the stack in C#?
No. That is a common simplification. Structs are value types, but where they are stored depends on context.
Are structs faster than classes?
Not always. Small structs can be efficient, but large structs may be expensive to copy. Choose based on meaning first, then measure performance.
Should all small types be structs?
No. Small size helps, but the type should also have value semantics. If identity matters, use a class.
Why are immutable structs recommended?
Because structs are copied by value. If they are mutable, it is easy to accidentally modify a copy instead of the original.
Can a struct have methods in C#?
Yes. A struct can have methods, properties, constructors, and overrides. It is not limited to raw data.
What does "represents a single value" mean?
It means the whole struct should conceptually behave as one thing, even if it contains multiple fields, like a point or a date range.
When in doubt, should I use class or struct?
Usually class, unless you clearly need value semantics and the type is small and immutable.
Mini Project
Description
Build a small geometry model that demonstrates when a struct is a good fit and when a class is a better fit. You will create a Point as a value type and a Shape as a reference type to see how copying works differently in practice.
Goal
Create a C# program that shows value-copy behavior with a struct and shared-reference behavior with a class.
Requirements
- Create a
readonly structnamedPointwithXandYproperties. - Create a
classnamedShapewith aNameproperty and aPointproperty calledPosition. - Show that assigning one
Pointvariable to another creates an independent copy. - Show that assigning one
Shapevariable to another copies the reference. - Print the results so the difference is easy to see.
Keep learning
Related questions
AddTransient vs AddScoped vs AddSingleton in ASP.NET Core Dependency Injection
Learn the differences between AddTransient, AddScoped, and AddSingleton in ASP.NET Core DI with examples and practical usage.
Best Way to Repeat a Character in C#: Building Repeated Strings Efficiently
Learn the best way to repeat a character in C#, compare StringBuilder, string concatenation, and simpler built-in options.
C# Access Modifiers and static: public, private, protected, and Defaults
Learn how C# public, private, protected, and default access control visibility, and how static differs from instance members.