Intermediate · Classes and objects · topic 17 of 43 · 30 min
Structs, value types and reference types
Explanation
Two kinds of types
Every type in C# is either a value type or a reference type. The difference decides what happens when you copy a variable, pass it to a method, or compare it. Interviewers ask about this very often.
| Value type | Reference type | |
|---|---|---|
| Examples | int, double, decimal, bool, char, DateTime, enum, struct | class, string, array, List<T>, delegate |
| Variable holds | The data itself | A reference (address) to an object |
| Assignment b = a | Copies the whole value | Copies the reference; both share one object |
| Can be null? | No (unless written as int?) | Yes |
| Default value | 0, false, or all fields zero | null |
Seeing the difference
PointS s1 = new PointS { X = 1, Y = 2 };
PointS s2 = s1; // copies the values
s2.X = 10;
Console.WriteLine(s1.X); // 1
PointC c1 = new PointC { X = 1, Y = 2 };
PointC c2 = c1; // copies the reference
c2.X = 10;
Console.WriteLine(c1.X); // 10
struct PointS { public int X; public int Y; }
class PointC { public int X; public int Y; }Stack and heap: the honest, simple version
You will hear "value types live on the stack, reference types live on the heap". It is a useful first picture but not the full truth. The stack is fast memory for a method's local variables; the heap is memory managed by the garbage collector. A local int usually sits on the stack. Every class object lives on the heap. But an int field inside a class object lives on the heap, inside that object. So think in terms of copy versus share, which is always true, rather than stack versus heap, which depends on where the value is stored.
Writing your own struct
var a = new Money(100m, "INR");
var b = a.Add(new Money(50.5m, "INR"));
Console.WriteLine(b); // INR 150.50
public readonly struct Money
{
public decimal Amount { get; }
public string Currency { get; }
public Money(decimal amount, string currency)
{
Amount = amount;
Currency = currency;
}
public Money Add(Money other) => new Money(Amount + other.Amount, Currency);
public override string ToString() => $"{Currency} {Amount:F2}";
}When to choose a struct
- It is small (a few numbers), roughly 16 bytes or less.
- It represents a single value, like a point, a money amount, a colour or a date.
- It is immutable (never changes after creation).
- It is created in very large numbers, where avoiding heap objects helps performance.
If any of these is false, use a class. Most of your types in business apps will be classes. C# also has record struct (module 5), which is often the easiest way to write a small value type.
Boxing and unboxing
Boxing happens when a value type is stored as object (or an interface): .NET makes a copy of it inside a new heap object, a "box". Unboxing is the cast back to the value type. It works, but it costs time and memory in hot loops. Generic collections like List<int> avoid boxing; the old ArrayList boxed every int.
int n = 42;
object boxed = n; // boxing: a copy goes into a heap object
int back = (int)boxed; // unboxing
n = 7;
Console.WriteLine(boxed); // 42 (the box holds its own copy)Equality
For classes, == checks whether two references point to the same object (unless the class defines its own ==, as string does). For your own structs, == does not compile until you write the operator yourself, while Equals compares the field values. Records (module 5) give you value equality for free.
Common mistakes and fixes
- Changing a struct inside a List: list[0].X = 5 is error CS1612, because list[0] returns a copy. Read it, change the copy, and store it back, or better, make the struct immutable.
- Big mutable structs passed around: every pass copies all the fields. Make it a class.
- Expecting a method to change a struct argument: it gets a copy. Return the new value instead.
- Thinking string is a value type because it behaves like one: string is a reference type that is immutable and compares by content.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Structs, value types and reference types · 1/7
In this lesson you will learn the most important difference in C#: value types versus reference types.
Prefer a video by a teacher? Find free videos on “C# value types vs reference types struct vs class boxing explained” on YouTube ↗
In real life: GPS points in a cab app
A cab-hailing app handles millions of location updates from drivers every minute. Each location is just a latitude and a longitude, never changed after it is read, so a small readonly struct is a good fit and avoids creating millions of heap objects.
var pickup = new GeoPoint(12.9716, 77.5946); // Bengaluru
var drop = pickup.Move(0.01, 0.0);
Console.WriteLine(pickup);
Console.WriteLine(drop);
public readonly struct GeoPoint
{
public double Lat { get; }
public double Lng { get; }
public GeoPoint(double lat, double lng) { Lat = lat; Lng = lng; }
public GeoPoint Move(double dLat, double dLng) => new GeoPoint(Lat + dLat, Lng + dLng);
public override string ToString() => $"({Lat:F4}, {Lng:F4})";
}Practice (0/4 done)
Which of these is a value type?
What does this print? var a = new P { V = 1 }; var b = a; b.V = 2; Console.WriteLine(a.V); struct P { public int V; }
int n = 5; object o = n; n = 9; Console.WriteLine(o); What is printed?
Write a readonly struct Temperature with a double Celsius property, a constructor, a method ToFahrenheit() returning double (C * 9 / 5 + 32), and a method Warmer(double by) that returns a NEW Temperature. Show that the original does not change.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Properties: get, set, init and requiredClasses and objects · same module
- Records and pattern matchingInheritance, interfaces and records
- Pointers to structs and the arrow operatorC Programming (from zero to systems) · Structs, unions, enums and files
- Structs and methodsGo Programming (backend and cloud) · Collections and structs
- Wrapper classes and autoboxingJava Programming (Core to Spring Boot) · Arrays and strings
- References, copies and equalityJavaScript Programming (language in depth) · Arrays and objects
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
Assigning one class variable to another...
Answer: Copies the reference, so both refer to the same object
Classes are reference types.
Which statement is most accurate?
Answer: Value types are copied on assignment; where they are stored depends on context
A value-type field inside a class object lives on the heap. Copy semantics is the reliable rule.
What is boxing?
Answer: Wrapping a value type in a heap object when it is treated as object
Boxing makes a heap copy of a value type.
When is a struct a good choice?
Answer: For small, immutable, value-like data such as a point or money
Small, immutable values are the classic struct use.
What is the default value of a reference-type field?
Answer: null
Reference-type fields default to null.
Is record struct a value type or a reference type?Related
Can a struct contain a member of its own struct type (not a pointer)?Related
Answer: No: it would be infinitely large; it can contain a pointer to its type
Self-reference must be through a pointer.
Which type holds true or false?Related
What does assigning one struct variable to another do?Related
Answer: Copies all the fields
Structs are values; assignment copies every field.
From Structs and methods
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- This is the last topic of “Classes and objects”. The module exam comes next.
- Module 4 exam: Classes and objects · 13 questions · 20 min, without help. 4 topics of this module still to finish first.
- Then the next module, Inheritance, interfaces and records. 43 topics left in the course before the final exam.
← Previous: Access modifiers, static members and encapsulation