Question

When to Use struct vs class in C#: Value Types Explained

csharpstruct

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 struct should represent a single value.
  • A struct should have a memory footprint smaller than 16 bytes.
  • A struct should 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 Point with X = 10 and Y = 20 is usually just a value. If two points have the same coordinates, they are effectively the same value.
  • A Customer is 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:

  • DateTime
  • TimeSpan
  • Guid
  • Point
  • Rectangle
  • decimal

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 struct for small, immutable, value-like data.
  • Choose class for 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.

Take Quiz

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

  1. p1 is created with values X = 1 and Y = 2.
  2. p2 = p1; copies the entire struct value.
  3. Now p1 and p2 contain the same data, but they are separate copies.
  4. p2.X = 99; changes only p2.
  5. p1 stays 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

  1. p1 refers to one object in memory.
  2. p2 = p1; copies only the reference.
  3. Both p1 and p2 point to the same object.
  4. p2.X = 99; changes that shared object.
  5. Reading through p1 shows 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:

  • User
  • Order
  • Product
  • Invoice

These usually have identity and lifecycle.

Services and managers

Examples:

  • EmailService
  • CacheManager
  • OrderProcessor

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.

Take Quiz

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

Featurestructclass
Type categoryValue typeReference type
AssignmentCopies the valueCopies the reference
IdentityUsually no separate identityUsually has identity
MutationBest kept immutableOften mutable
Typical sizeSmallAny size
Default use caseCoordinates, dates, small value objectsEntities, services, complex state
Heap allocationOften avoided in simple casesUsually allocated as objects
InheritanceCannot inherit from another struct/classSupports inheritance

struct vs record class vs record struct

TypeBest for
classReference-based objects with identity or mutable shared state
structSmall value-like types, often immutable
record classReference types with value-based equality semantics
record structValue types with concise syntax and value-like behavior

Simple decision guide

  • Use struct if the type is:

    • small
    • immutable
    • value-like
    • not identity-based
  • Use class if the type is:

    • large
    • mutable
    • shared across code
    • identity-based
    • behavior-rich or lifecycle-driven
Take Quiz

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 struct only 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.

Take Quiz

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

  1. Create a readonly struct named Point with X and Y properties.
  2. Create a class named Shape with a Name property and a Point property called Position.
  3. Show that assigning one Point variable to another creates an independent copy.
  4. Show that assigning one Shape variable to another copies the reference.
  5. Print the results so the difference is easy to see.
Take Quiz

Keep learning

Related questions

View more