Skip to main content
๐Ÿ“œ WAYPOINT LESSON

Value vs Reference, Boxing, and Identity

โญโญ intermediateโณ 16 min read๐Ÿ“ Lesson 86 of 265

What actually copies, what actually aliases, when structs get boxed, and why Equals differs from ==.

Two families, two behaviors

A value type (struct, enum, numeric types, bool, char) carries its data inline. Assignment copies the whole value. A reference type (class, record class, interface, delegate, array, string) stores a reference; the object lives on the heap, and assignment copies only the reference โ€” two variables then alias one object.

struct PointS { public int X; }
class  PointC { public int X; }

var s1 = new PointS { X = 1 };
var s2 = s1; s2.X = 9;              // s1.X is still 1 โ€” copy

var c1 = new PointC { X = 1 };
var c2 = c1; c2.X = 9;              // c1.X is now 9 โ€” alias

Neither is "better". Use structs for small, immutable, leaf values (coordinates, money amounts, identifiers); classes for entities with identity and lifetime. record struct gives you value semantics with generated equality; record (class) gives reference semantics with value-based equality โ€” handy but distinct things.

Boxing: the invisible heap allocation

Boxing wraps a value type in a heap object so it can travel where a reference is expected. It happens silently in three common places:

object o = 42;                       // explicit box
IComparable<int> c = 7;              // interface box
Console.WriteLine(string.Join(" ", 1, 2, 3));   // params object[]: each int boxes

Unboxing is the reverse cast โ€” it must name the EXACT type: unboxing a boxed int as double throws InvalidCastException, even though int โ†’ double converts implicitly in normal code.

Boxing costs allocation and hurts in hot loops. Console.WriteLine(42) does NOT box (it has an int overload) โ€” but string.Format("{0}", x) with an object[] path does. Equality gets subtle too: boxed values compare by value with Equals, but == on object compares references:

object a = 1000, b = 1000;
a.Equals(b)      // true  โ€” boxed int compares by value
a == b           // false โ€” different boxes
(object)a == (object)1000   // false again

Check your understanding

  • DateTime is a struct: after d2 = d1; d2 = d2.AddDays(1), what is d1? (Unchanged โ€” AddDays returns a new value.)
  • Why does ArrayList (pre-generics) feel slow for ints? (Every Add boxes.)
  • What does (short)(object)intVar throw? (InvalidCastException โ€” exact-type rule.)

โšก Now practice

Ready to Code
Semantics GymValue vs reference, boxing identity, and honest nullability until the model in your head matches the runtime.
3 challenges ยท ยท ~26 min