Struct Vs Class When To Use Which Β· Additional notes
3 min read- Additional notes
- βοΈ Practical Explanation (How CLR Handles Them)
- π§© `struct`
- π§© `class`
- β‘ Performance and Design Implications
- β When to use `struct`
- π« When NOT to use `struct`
- β οΈ Boxing and Hidden Allocations
- π§© Memory Visualization
- 1οΈβ£ Basic memory layout
- 2οΈβ£ Assignment behavior
- β Struct (value type)
- β Class (reference type)
- 3οΈβ£ Struct inside a class (inline layout)
- 4οΈβ£ Passing to methods
- 5οΈβ£ Heap fragmentation and GC difference
- π§ Quick βwhiteboard pitchβ for your interview
Additional notes
βοΈ Practical Explanation (How CLR Handles Them)
π§© struct
- Lives inline β if itβs a local variable, itβs on the stack; if itβs a field in another object, itβs inside that objectβs memory layout.
- When passed to a method, a full copy is made (unless passed by
reforin). - Ideal for small, immutable, lightweight data β e.g., coordinates, ticks, prices, GUIDs.
Example:
struct Point
{
public int X;
public int Y;
}
Each Point lives inline β no GC pressure.
π§© class
- Lives on the managed heap. Variables hold a reference (pointer) to the actual object.
- Passed around by reference, so multiple variables can point to the same instance.
- Managed by the Garbage Collector.
Example:
class Order
{
public string Symbol { get; set; }
public double Price { get; set; }
}
Each Order allocation hits the heap and is tracked by the GC.
β‘ Performance and Design Implications
β
When to use struct
Use when:
- The object is small (β€ 16 bytes typically).
- Itβs immutable.
- Youβll create many of them (e.g., millions per second) and want no GC overhead.
- Value semantics make sense (copying creates independence).
Example (trading context):
readonly struct Tick
{
public string Symbol { get; }
public double Bid { get; }
public double Ask { get; }
}
Each Tick represents an immutable market data point. Perfect as a struct.
π« When NOT to use struct
Avoid when:
- Itβs large (lots of fields) β copying becomes expensive.
- You need polymorphism, inheritance, or shared references.
- You mutate the same instance in multiple places.
β οΈ Boxing and Hidden Allocations
When a struct is treated as an object or cast to an interface, it gets boxed β copied onto the heap.
struct Point { public int X, Y; }
object obj = new Point(); // BOXED: allocates on heap
Point p = (Point)obj; // UNBOXED: copy back to stack
So: value types are not automatically zero-GC β you must use them carefully.
π§© Memory Visualization
Stack:
ββ Tick t1 { X=1, Y=2 } (struct: inline)
ββ Tick t2 = t1 (copied!)
ββ Order ref ββ
βΌ
Heap:
ββ { Symbol="EURUSD", Price=1.0734 } (class: heap object)
1οΈβ£ Basic memory layout
ββββββββββββββββββββββββββββββββ
β Stack β
β βββββββββββββββββββββββββββ β
β β int x = 10; β β
β β Point p = {X=1,Y=2}; β β β Struct (value type)
β βββββββββββββββββββββββββββ β
β (lives inline here) β
β β
β βββββββββββββββββββββββββββ β
β β Order o βββββββββββββββββΌβββΌβββΊ Heap
β βββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββ
β Heap β
β βββββββββββββββββββββββββββ β
β β Order { Id=1, Price=99 }β β β Class (reference type)
β βββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββ
Explanation:
- Struct (
Point) is stored directly on the stack or inline within another object. - Class (
Order) is stored on the heap; variables on the stack hold a reference (pointer) to it.
2οΈβ£ Assignment behavior
β Struct (value type)
Point a = new Point { X = 1, Y = 2 };
Point b = a; // copy!
b.X = 99;
Console.WriteLine(a.X); // 1 (a unaffected)
Memory:
Stack:
a { X=1, Y=2 }
b { X=99, Y=2 } β completely separate copy
- Structs are copied by value.
- Each variable has its own independent copy.
- No heap allocation β no GC pressure.
β Class (reference type)
Order o1 = new Order { Id = 1, Price = 99 };
Order o2 = o1; // copy reference!
o2.Price = 120;
Console.WriteLine(o1.Price); // 120
Memory:
Stack:
o1 ββ
o2 βββββΊ Heap: { Id=1, Price=120 }
- Classes are copied by reference β both variables point to the same heap object.
- Modifying one affects the other.
3οΈβ£ Struct inside a class (inline layout)
class Trade
{
public string Symbol;
public Point Position;
}
Memory:
Heap: Trade
ββ Symbol β "EURUSD" (heap reference)
ββ Position { X=10, Y=20 } (inline in Trade object)
Insight: Even though the struct is inside a class (on heap), its fields are embedded inline β not separate allocations. This reduces pointer indirection and helps cache locality.
4οΈβ£ Passing to methods
void Move(Point p) { p.X += 10; } // copy!
void MoveRef(ref Point p) { p.X += 10; } // modifies original
Memory visualization:
By value (copy):
Caller: a { X=1 }
Method: p { X=1 } β modified to X=11 (copy destroyed)
By ref:
Caller: a { X=1 }
Method: p ββ
ββ modifies same memory β X=11 persists
π‘ Interview tip:
βStructs are copied on method calls unless passed by
reforin. Large structs should be passed byinto avoid copy overhead β especially in tight loops or latency-critical code.β
5οΈβ£ Heap fragmentation and GC difference
Structs:
[Stack]
[Stack frame destroyed β data gone instantly]
β No GC involvement.
Classes:
[Heap]
[Objects live until unreachable]
β GC scans and collects them (Gen0βGen1βGen2)
Key insight:
- Structs vanish when they go out of scope β predictable lifetime.
- Classes depend on GC cycles β non-deterministic reclamation.
- Overusing classes in a high-frequency path (like market ticks) causes GC churn and pauses.
π§ Quick βwhiteboard pitchβ for your interview
βStructs are value types β stored inline, copied by value, no GC involvement, ideal for small immutable data like ticks or coordinates. Classes are reference types β heap-allocated, reference-based, and managed by GC. I use structs where I want predictable lifetimes and zero allocations; classes when I need shared, long-lived state or polymorphism.β
Would you like me to now create a visual of memory layout with stack/heap arrows (an actual diagram you could memorize or even sketch during the interview)? It would show struct, class, and mixed cases (struct-in-class, class-in-struct) clearly.