class
in C#class
objects store and copy datastruct
in C#struct
objectsstruct
objects often immutableclass
and struct
class
vs. struct
class
and struct
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:
class
and struct
work under the hoodBy 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.
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.
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.
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.
class
objectsWhile 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.
class
objectsDespite 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.
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.
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:
struct
is declared inside a method, it’s stored on the stackstruct
is a field inside a class
, it’s stored on the heap (because the class
itself is stored in the heap)struct
is part of an array or collection, it follows the allocation rules of that collectionFor 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?
Since large struct
objects are expensive to copy, C# best practices recommend keeping them small.
General rule of thumb:
struct
has just a few fields (e.g., int
, float
, bool
), it’s finestruct
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
}
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.
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:
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.
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 |
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.
class
when:Example: A GameManager
that tracks game progress should be referenced by all characters, rather than copied each time
Example: A CustomerOrder
object with dozens of properties and nested structures would be inefficient to duplicate every time it's passed to a method
Example: A Vehicle
class that can be extended into Car
, Bike
, and Truck
needs to be a class
struct
when:Example: A Color
struct that holds RGB values should be independent and not accidentally modified when passed to a method
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
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.
Here are the biggest pitfalls developers run into, why they happen, and how to fix them.
struct
for large objectsOne 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.
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.
struct
, pass it by referenceUsing 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)
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:
struct
objects copy by value, this approach makes that behavior explicitclass
for small, short-lived objectsA 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:
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.
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.
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:
By keeping these in mind, you’ll make better choices that improve performance, readability, and maintainability in your C# applications.
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.
Don’t forget - if you want to learn more about C# as well as the .NET platform, check out my complete course:
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.
If you enjoyed this post, check out my other C# tutorials: