C#64 min total · 17 parts
C# Fundamentals: Value Types, Reference Types, Boxing, and the Type System
Part 3 of 17 · ~2 min
The Stack/Heap Misconception
"Value types live on the stack, reference types live on the heap" is the shorthand everyone learns, and it's close enough to be useful — right up until it isn't, which tends to be exactly where an interviewer probes next.
The actual rule doesn't mention value-vs-reference at all — it just asks where the variable sits. A StockCount declared as a method's local variable gets stack storage, because that's what ordinary locals get. Put the identical struct behind a class field instead, and it rides along inside that object's own heap block — there's no separate stack slot reserved for it, and never was one:
class ShipmentLine
{
public StockCount Snapshot; // this StockCount lives on the heap, inline inside the ShipmentLine object
}
void RecordPick()
{
var local = new StockCount { Sku = "WDG-100", OnHand = 41 }; // lives on the stack
var line = new ShipmentLine(); // the ShipmentLine object lives on the heap
line.Snapshot = local; // this StockCount is now part of that heap allocation
}
local and line.Snapshot are carrying identical numbers right after that assignment runs, and they're still sitting in two different kinds of memory — nothing about StockCount itself changed, only where the specific variable holding a copy of it happens to live. A variable a lambda closes over, or one still needed on the far side of an await, gets the same heap treatment for the same reason: it has to outlast the function call that declared it, and the stack frame that call was using is long gone by the time execution comes back.
Notice these are two independent questions with two independent answers. "Where does the memory sit" is about stack versus heap. "What does assignment do" is about copy versus share. They only ever look like the same question for an ordinary local variable, where the answers happen to line up — which is exactly why the shorthand feels true right up until a value type stops being a plain local, at which point the two questions come apart and the shorthand starts giving you the wrong prediction.