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.