Learning outcome
After this lesson, you should be able to explain why two variables sometimes behave like separate copies and sometimes behave like two names for the same object. In C#, the key idea is not “stack versus heap”; it is whether a variable holds a value directly or holds a reference to an object. You will also learn that a struct can contain references, so copying a value type may still leave both copies pointing at the same mutable object. Finally, you will distinguish ordinary by-value assignment from ref aliases, which are a different language feature.
Intuition
Think of a variable as a labeled container. For a value type, the container holds the data itself. If you copy that variable, you get a new container with its own data. For a reference type, the container holds a reference to an object. If you copy that variable, you copy the reference, not the object itself, so both variables point to the same object.
That means two separate questions matter:
- Did I copy the value, or did I copy a reference?
- Does the thing being copied contain only simple data, or does it contain references to other objects?
A common beginner mistake is to say, “value types are on the stack and reference types are on the heap.” That is too simplistic and often misleading. C# assignment behavior is about semantics, while memory placement is an implementation detail of the runtime.
Plain-language example 1: independent numeric copies. If you assign one int to another, the second variable gets its own number. Changing one does not change the other.
Plain-language example 2: shared mutable list reference. If you assign one List<string> to another, both variables refer to the same list. Adding an item through one variable is visible through the other, because there is only one list object.
Deep dive
The C# type system divides types into value types and reference types. Value types include built-in numeric types, bool, char, enums, tuples, and struct/record struct types. Reference types include classes, arrays, delegates, and string. The distinction affects assignment, parameter passing, and return values.
For ordinary assignment, C# copies the source variable’s value. With value types, that means the actual data is copied. With reference types, that means the reference is copied. So if two variables refer to the same mutable object, changes through one variable can be observed through the other.
A useful mental model:
int,double,bool, and other simple value types: copying makes a new independent value.classinstances and collections likeList<T>: copying makes another reference to the same object.structinstances: copying duplicates the struct’s fields, but any fields that are themselves references still point to the same referenced objects.
This last point is subtle and very important. A value type is not automatically “fully independent” if it contains reference-type fields. Copying the struct copies the field values, and if one of those field values is a reference, both struct copies now share the same referenced object. That is why mutable value types can be tricky.
Another separate mechanism is ref aliasing. A ref local or ref parameter does not behave like a normal copy. It creates an alias to an existing variable, so operations through the alias act on the original variable itself. This is not the same as ordinary assignment, and you should not confuse it with “value vs reference type.” A ref alias is about how a variable is bound, not about whether the underlying type is a value type or a reference type.
When choosing types in your own code, a good rule of thumb is to prefer immutable value types for small data and use reference types for shared identity and complex behavior. If you need shared mutable state, a reference type is often the clearer choice. If you want independent data copies, a value type is often the better fit.
Failure modes
Common mistakes to avoid:
- Saying that value types “live on the stack” and reference types “live on the heap” as a universal rule. That is not what the C# type distinction means.
- Assuming every copy of a
structis completely isolated. If the struct contains references, shared mutable state can still exist. - Thinking
stringbehaves like a mutable reference type.stringis a reference type, but its contents are immutable, so sharing a string is usually safe. - Confusing ordinary assignment with
refaliasing. A normal assignment copies; arefdoes not. - Forgetting that arrays are reference types, even though they contain value elements or reference elements depending on their element type.
Interview drill
Try answering these in plain English:
- What is copied when you assign one
intvariable to another? - What is copied when you assign one
List<int>variable to another? - Why can two copies of a
structstill appear to share state? - How is
refdifferent from ordinary assignment? - Why is “stack vs heap” not a correct way to define value types versus reference types?
A strong answer mentions value copying, reference copying, object identity, and the fact that memory location is separate from the language distinction.
Revision checklist
- I can explain that value types copy data directly.
- I can explain that reference types copy a reference to an object.
- I know that assigning a reference type variable does not clone the object.
- I know that a struct can still share referenced objects inside its fields.
- I can distinguish ordinary assignment from
refaliasing. - I do not describe value types as “the stack” or reference types as “the heap.”
- I can give one example of independent numeric copies and one example of shared mutable list references.