Use Code: ZTMBDAY2025 to get 27% OFF any membership. Expires soon 👇

Beginners Guide To C# Struct vs Class (With Code Examples)

Claudio Bernasconi
Claudio Bernasconi
hero image

When you're learning C#, you'll quickly run into class and struct, and at first glance, they seem almost identical — you can use both to define custom data types, store fields and methods, and create objects.

But here’s the thing: choosing between a class and a struct isn’t just about preference. It affects how your program handles memory, performance, and data sharing. Pick the wrong one, and you might run into unexpected bugs or slowdowns.

So, how do you decide?

In this guide, you’ll learn:

  • How class and struct work under the hood
  • How memory usage and performance differ between them
  • When to use each one (and when to avoid them)
  • Common mistakes beginners make and how to avoid them

By the end, you'll know exactly when to use a class and when a struct is the better choice for your code.

Let’s dive in!

Sidenote: If you want to learn more about C#, then check out my C#/.NET Bootcamp course.

learn c sharp

This is the only course you need to learn C# programming and master the .NET platform. Plus, Claudio just recently added a massive new sections to teaching your how to build web applications with ASP.NET Core MVC, Razor Pages, and Blazor.

No previous coding experience required - you’ll learn C# programming from scratch, including powerful skills like data structures, object-oriented programming (OOP), and testing.

All while building your own projects, so you can get hired as a C#/.NET Developer in 2025!

Click the image or image above to check it out, or just start watching the first few lessons here for free (no signup or credit card needed, just start learning).

With that out of the way, let’s get into this 5-minute tutorial.

What is a class in C#?

A class in C# is a blueprint for creating objects. Think of it like a template — you define the structure once, then create as many objects from it as you need.

At first, it seems pretty simple. You group related data and behavior into a class, then create objects that use it. But what’s happening behind the scenes completely changes how your program behaves.

Unlike some types in C#, a class doesn’t store values directly inside a variable. Instead, it stores a reference to an object in memory. That means when you assign a class instance to another variable, you’re not copying the data — you’re just copying a reference to the same object.

If that sounds a little abstract, let’s break it down.

How class objects store and copy data

Class objects are reference types, which means they live on the heap, which is a section of memory that stores objects for the long term. The heap allows objects to persist beyond the method where they were created, but there’s a catch: objects stay there until the garbage collector decides to remove them.

Why does this matter?

Because when you assign a class object to another variable, you’re not creating a new copy — you’re just pointing another reference to the same object in memory.

For example

class Person
{
	public string Name;
	public int Age;
}

Person p1 = new Person();
p1.Name = "Alice";
p1.Age = 30;

Person p2 = p1; // Copies the reference, NOT the object

p2.Age = 35; // Modifies the same object in memory

Console.WriteLine(p1.Age); // Output: 35

At first, you might think p1 and p2 are independent objects. But because Person is a class, both variables reference the same object in memory. So when p2.Age is updated, p1.Age changes too.

Think of it like a shared Google Doc.

If multiple people have access, everyone sees the same document. If one person edits it, the changes appear for everyone else. That’s exactly how class objects work. When one part of your program modifies the object, every other reference to that object sees the change.

This can be useful when you need different parts of your program to work with the same data, but it can also introduce unexpected side effects. If you change an object in one place, you might accidentally affect other parts of your program that are using the same reference.

The downsides of class objects

While class objects are powerful, they come with some trade-offs:

Garbage collection overhead – Objects persist in memory until garbage collection decides to remove them. If your program creates a lot of objects without cleaning up properly, performance can take a hit.

Memory leaks – If an object is still referenced somewhere in your program, it won’t be garbage collected, which can lead to memory leaks and increased memory usage.

The benefits of class objects

Despite those downsides, class objects are the backbone of most C# applications. Here’s why:

Efficient for large objects – Instead of copying data every time an object is passed around, a reference is used, making class objects ideal for handling complex structures.

Enable shared state – Since multiple variables can reference the same object, different parts of your program can work with the same data without unnecessary duplication. This is useful for database connections, configuration objects, and UI components.

Support object-oriented programming (OOP)Class objects allow inheritance (where one class extends another) and polymorphism (where different class objects can be used interchangeably), making them essential for building flexible, reusable code.

I’ll cover some of this in more detail when we look at common mistakes, but before that, let’s take a look at struct, and then compare the two and when to use one vs the other.

What is a struct in C#?

A struct in C# is a lightweight value type designed for storing small, self-contained pieces of data.

The confusion around struct vs. class often comes from the fact that, at first glance, they seem very similar — both allow you to define fields, properties, and even methods. However, the way a struct stores and manages memory is completely different.

Unlike class objects, which live on the heap, a struct stores its data directly inside the variable. This means every time you assign a struct to a new variable, you're making an independent copy, rather than referencing the same object.

This might seem like a small difference, but it fundamentally changes how struct objects behave in terms of memory usage, performance, and copying behavior.

Why does this matter?

Because when you assign a struct to another variable, a complete copy of the data is made. This prevents unintended modifications and makes struct objects safer for handling independent pieces of information.

For example

struct Point
{
	public int X;
	public int Y;
}

Point p1 = new Point();
p1.X = 10;
p1.Y = 20;

Point p2 = p1; // A copy of p1 is created

p2.X = 50; // This only changes p2, NOT p1

Console.WriteLine(p1.X); // Output: 10 (p1 remains unchanged)

If Point were a class, p1 and p2 would reference the same object, so modifying p2.X would also change p1.X. But because Point is a struct, p2 gets its own copy, so changes to p2.X don’t affect p1.X at all.

This makes struct objects safer because you don’t have to worry about accidentally modifying shared data. But there’s a trade-off in that copying large struct objects can slow things down.

What about memory? Are struct objects always stored on the stack?

A common misconception is that struct objects always live on the stack. While this is often true for local variables, it isn’t always the case. Where a struct is stored depends on where it’s used:

  • If a struct is declared inside a method, it’s stored on the stack
  • If a struct is a field inside a class, it’s stored on the heap (because the class itself is stored in the heap)
  • If a struct is part of an array or collection, it follows the allocation rules of that collection

For example

struct Point
{
	public int X;
	public int Y;
}

class Container
{
	public Point Position; // This struct is stored inside a class
}

// This struct is allocated inside the heap along with the Container object
Container obj = new Container();

Even though Point is a struct, when it’s placed inside a class, it doesn’t stay on the stack. Instead, it moves to the heap along with the Container object.

This means where a struct is stored depends on how it’s used, but storage isn’t the only concern.

If you want to get the most out of struct objects, you need to keep them small. Since struct objects always copy their data, larger structs can hurt performance by creating unnecessary memory overhead. (More on this later).

So, how do you make sure your struct objects are efficient?

How to avoid performance issues with struct objects

Since large struct objects are expensive to copy, C# best practices recommend keeping them small.

General rule of thumb:

  • If a struct has just a few fields (e.g., int, float, bool), it’s fine
  • If a struct contains large objects, arrays, or nested structs, consider using a class instead
// This is fine - small struct
struct Rectangle
{
	public int Width;
	public int Height;
}

// This is problematic - large struct
struct LargeData
{
	public int[] Numbers; // Large array inside a struct
	public string Name;   // Reference type inside a struct
}

TL;DR

If a struct is larger than 16 bytes, or if it’s frequently passed around, a class is usually the better choice. That’s why most struct objects represent small, independent values like coordinates, colors, or lightweight data types that don’t need to be modified frequently.

But there’s another reason modifying struct objects frequently can be a problem — every change creates a copy, which can hurt performance.

Why are struct objects often immutable?

Since modifying a struct creates a new copy, excessive modifications can reduce performance and lead to unexpected behavior.

To avoid this, struct objects are often designed to be immutable, meaning once they are created, their values never change.

For example

Here you can see modifying a struct inside a method.

struct Point
{
	public int X;
	public int Y;
}

void ModifyPoint(Point p)
{
	p.X = 100; // This changes a copy, NOT the original
}

Point myPoint = new Point { X = 10, Y = 20 };
ModifyPoint(myPoint);

Console.WriteLine(myPoint.X); // Output: 10 (unchanged)

Even though p.X was modified inside ModifyPoint(), it had no effect on myPoint. That’s because a copy of the struct was passed into the method, not the original.

This is exactly why modifying struct objects can sometimes lead to unexpected behavior, because you think you're updating a value, but you're actually working with a separate copy.

So to prevent accidental modifications, struct objects are often designed to be immutable, meaning their values never change after they’re created.

One way to enforce immutability is by marking the struct as readonly, which ensures that all fields stay the same after initialization.

For example

readonly struct Point
{
	public int X { get; }
	public int Y { get; }

	public Point(int x, int y)
	{
    	X = x;
    	Y = y;
	}
}

With this setup:

  • Data remains consistent – No accidental modifications
  • Performance is improved – Avoids unnecessary copying
  • Safer code – Prevents unexpected changes when passing structs around

Now that you’ve seen how struct objects store data differently from class objects, let’s break down their key differences side by side so you can decide exactly when to use each one.

Key differences between class and struct

Each one has its strengths and weaknesses, and choosing the right one depends on how your data will be used.

Feature class (Reference Type) struct (Value Type)
Memory Allocation Stored on the heap, managed by the garbage collector Stored on the stack (or inlined in objects) and automatically cleaned up
Copying Behavior Assigning a class object copies the reference, meaning multiple variables point to the same object Assigning a struct copies the entire value, making each variable independent
Performance Heap allocation is flexible but requires garbage collection, which adds overhead Stack allocation is faster for small objects, but copying large struct objects can be inefficient
Mutability class objects are mutable by default, meaning changes affect all references struct objects should be immutable whenever possible to avoid unintended copying. However they are usually mutable by default
Object Size Ideal for large objects, since only references are passed around Best for small, lightweight objects to avoid excessive copying
Inheritance Supports inheritance and polymorphism Cannot be inherited, but can implement interfaces

When to use a class vs. a struct

Both class and struct have their place, but choosing the wrong one can impact performance, memory usage, and program behavior. On the flip side, choosing the right one can improve performance and optimize memory usage and management.

Use a class when:

  • The object needs to be shared across multiple parts of the program

Example: A GameManager that tracks game progress should be referenced by all characters, rather than copied each time

  • The object is too large to be copied efficiently

Example: A CustomerOrder object with dozens of properties and nested structures would be inefficient to duplicate every time it's passed to a method

  • You need object-oriented features like inheritance and polymorphism

Example: A Vehicle class that can be extended into Car, Bike, and Truck needs to be a class

Use a struct when:

  • The object represents a simple value that doesn’t need to be shared or modified

Example: A Color struct that holds RGB values should be independent and not accidentally modified when passed to a method

  • The object is small and frequently created/destroyed

Example: A Vector3 struct in a game engine that represents a 3D point is better as a struct because thousands of them might be created every frame

  • Performance is critical, and you want to avoid garbage collection

Example: A Rectangle struct used for basic UI calculations won’t trigger unnecessary heap allocations

Even when you understand the differences between class and struct, it’s easy to run into subtle issues that cause performance problems or unexpected bugs.

So, before we wrap up, let’s look at some of the most common mistakes and how to avoid them.

Common mistakes when using class and struct

Here are the biggest pitfalls developers run into, why they happen, and how to fix them.

Mistake #1. Using a struct for large objects

One of the biggest mistakes is assuming struct objects are always faster because they don’t use garbage collection. While it’s true that stack allocation is efficient, this only applies when the struct is small.

Why is this a problem?

When a struct is assigned to a new variable, a full copy is made. If the struct contains a lot of data, these copies add up fast, leading to performance issues.

For example

struct LargeStruct
{
	public int[] Data; // Large array stored inside a struct
	public string Name; // Reference type inside a struct
}

Every time LargeStruct is assigned, the entire array and string get copied, causing unnecessary memory overhead.

LargeStruct s1 = new LargeStruct();
LargeStruct s2 = s1; // Copies the entire struct, including large data

How to fix it?

If an object has more than a few fields or contains reference types like arrays or strings, it should probably be a class instead.

For example

class LargeClass
{
	public int[] Data;
	public string Name;
}

Now, only the reference to the object is copied, not the entire data structure.

Mistake #2. Expecting a struct to behave like a class (Unexpected copying behavior)

If you're used to working with class objects, it's easy to assume that struct objects behave the same way. But struct objects are value types, which means they don’t share the same reference when assigned or passed to a method.

Why does this matter?

When you pass a struct to a method, a copy of it is made, so any changes inside the method only affect that copy, not the original.

For example

struct Point
{
	public int X;
	public int Y;
}

void MoveRight(Point p)
{
	p.X += 10; // Modifies the copy, not the original
}

Point myPoint = new Point { X = 5, Y = 5 };
MoveRight(myPoint);

Console.WriteLine(myPoint.X); // Output: 5 (not 15!)

At first, you might expect MoveRight(myPoint) to update myPoint.X, but because Point is a struct, a new copy is passed into the method. The method updates the copy, but when it returns, the original myPoint remains unchanged.

How to fix it? Well, that depends on whether you want to modify the original struct or not.

Solution: If you need to modify the original struct, pass it by reference

Using the ref keyword ensures that the method works with the original struct, not a copy.

For example

void MoveRight(ref Point p)
{
	p.X += 10; // Now modifies the original struct
}

MoveRight(ref myPoint);
Console.WriteLine(myPoint.X); // Output: 15 (expected behavior)

Solution: If your struct isn’t meant to be modified, make it immutable

Instead of modifying the original struct, return a new instance with updated values.

For example

struct Point
{
	public int X { get; }
	public int Y { get; }

	public Point(int x, int y)
	{
    	X = x;
    	Y = y;
	}

	public Point MoveRight() => new Point(X + 10, Y);
}

With this approach:

  • The struct stays immutable – No unexpected modifications
  • Code is clearer – You always create a new instance instead of modifying the existing one
  • Avoids confusion – Since struct objects copy by value, this approach makes that behavior explicit

Mistake #3. Overusing class for small, short-lived objects

A common mistake is defaulting to class for everything - even when a struct would be more efficient. While class objects are flexible, they aren’t always the best choice, especially for small, frequently created objects.

Why is this a problem?

Every time you create a class object, it’s allocated on the heap. If your program constantly creates and discards these objects, garbage collection has to clean them up, which can slow things down.

For example

class Point // No real need for this to be a class
{
	public int X;
	public int Y;
}

If you create millions of Point objects inside a loop, that’s millions of heap allocations. This puts extra pressure on the garbage collector, slowing down your program.

void Run()
{
	for (int i = 0; i < 1000000; i++)
	{
    	Point p = new Point(); // Creates millions of heap allocations
	}
}

How to fix it?

If the object is small, doesn’t need to be shared, and is frequently created/destroyed, use a struct instead.

Why does this work better?

Since struct objects are stack-allocated, they automatically disappear when they go out of scope.

This means:

  • No garbage collection overhead – The memory is cleaned up automatically
  • Faster performance – The stack is more efficient for short-lived objects

For example

struct Point // Now a value type
{
	public int X;
	public int Y;
}

By using a struct instead of a class, you avoid unnecessary heap allocations and keep performance high.

Mistake #4. Accidentally boxing a struct

If you're using a struct for performance reasons, be careful as you might accidentally trigger boxing, which moves the struct to the heap and defeats the whole purpose of using it.

Why is this a problem?

Boxing happens when a struct is converted into an object. Since objects live in the heap, this creates an unnecessary memory allocation and garbage collection overhead.

This often happens without you realizing it — especially when passing a struct to a method expecting an object:

For example

struct Point
{
	public int X;
	public int Y;
}

void Print(object obj)
{
	Console.WriteLine(obj.ToString());
}

Point p = new Point { X = 10, Y = 20 };
Print(p); // Implicit boxing occurs here!

Since Print(object obj) expects an object, Point gets boxed automatically, causing a hidden performance hit.

How to fix it?

To prevent boxing, make sure your struct stays a value type by using generics and type-safe collections.

For example

Use generics instead of object parameters:

void Print<T>(T value)
{
	Console.WriteLine(value.ToString());
}

Use generic collections instead of ArrayList:

List<Point> points = new List<Point>(); // No boxing occurs

These changes keep your struct on the stack, avoiding unnecessary heap allocations.

TL;DR

The biggest mistakes when choosing between struct and class usually happen when developers don’t fully consider how memory, copying, and modification work.

To avoid these issues:

  • Don’t use a struct if it’s large or frequently modified
  • Don’t use a class if it’s small and short-lived
  • Be aware of implicit copying with structs
  • Watch out for unintended boxing

By keeping these in mind, you’ll make better choices that improve performance, readability, and maintainability in your C# applications.

See which works best in your own code

Now that you know how struct and class behave, the best way to solidify your understanding is to test them yourself!

Try creating a struct and a class with the same fields—see how they behave when assigned to new variables or passed into methods. Do changes persist, or do they get lost? Measure performance in a loop (for example using BenchmarkDotNet) and observe memory usage.

By experimenting, you’ll quickly spot the key differences and avoid common pitfalls in your own code and always make the right choice.

P.S.

Don’t forget - if you want to learn more about C# as well as the .NET platform, check out my complete course:

learn c sharp

No previous coding experience required - you’ll learn C# programming from scratch, including powerful skills like data structures, object-oriented programming (OOP), and testing. All while building your own projects, so you can get hired as a C#/.NET Developer in 2025!

It’s the only course you need to learn C# programming and master the .NET platform. You’ll learn everything from scratch and put your skills to the test with exercises, quizzes, and projects!

Plus, once you join, you'll have the opportunity to ask questions in our private Discord community from me, other students, and working developers.


There's always someone online 24/7 happy to help. It's by far the thing that my students always tell me is the best part of their experience. Hope you decide to take my course and if you do, make sure to come say hi on Discord!

Check it out above, or watch the first few videos here for free.

More Beginner 5-Minute C# Tutorials

If you enjoyed this post, check out my other C# tutorials:

More from Zero To Mastery

5 Reasons Why You Should Learn C# preview
5 Reasons Why You Should Learn C#

It's been 23 years since C# went live, but it's still growing in popularity. Find out why (+ why you should learn this language ASAP to advance your career).

Best Programming Languages To Learn In 2025 preview
Best Programming Languages To Learn In 2025

Want to know the best programming languages to learn in 2025? See the top 10 picks for high-paying jobs, future-proof skills, and unstoppable growth.

What Is C# Used For? Pretty Much Everything! preview
What Is C# Used For? Pretty Much Everything!

C# can build anything from Desktop + Web Apps to Cloud Integrations, Games, Automations + more. In this guide, I walk through why it works so well in each area.