๐ WAYPOINT LESSON
ref, out, in, params โ The Passing Toolbox
โญโญ intermediateโณ 16 min read๐ Lesson 90 of 265
What each modifier promises, when it is honest design, and the aliasing hazards it creates.
The default: by value, always
C# passes arguments by value. For value types that means a copy of the data; for reference types, a copy of the reference โ which is why a method can mutate the object you passed but can never repoint your variable:
static void Replace(List<int> list) => list = new List<int>(); // caller unaffected
static void Fill(List<int> list) => list.Add(1); // caller sees the item
The modifiers, each with one job
outโ "this method will definitely assign your variable." The compiler enforces assignment-on-all-paths. One output (plus a bool success inTryXpatterns). Declare inline:int.TryParse(s, out int n).refโ "your variable's storage is shared." The method may read, write, or repoint it; the variable must be initialized first. Use for swap-like semantics or interlocked state โ rarely.inโreffor reading: a readonly reference to (usually) a large struct. The compiler guards against mutation. Use when copying a big struct costs measurably more than the indirection.paramsโ variadic tail:params int[] values, orparams ReadOnlySpan<int>in modern code. Zero-or-more arguments; must be last.
static bool TryDivide(int a, int b, out int result)
{
if (b == 0) { result = 0; return false; } // MUST assign on all paths
result = a / b;
return true;
}
static void Swap(ref int a, ref int b) => (a, b) = (b, a);
readonly struct Big { public readonly long A, B, C, D, E, F, G, H; }
static long Sum(in Big b) => b.A + b.B + b.C + b.D + b.E + b.F + b.G + b.H;
Return what you mean
Before reaching for out, ask: would a tuple return read better?
(bool Ok, int Value) carries the same information without aliasing โ but
out + TryX is the idiomatic .NET shape for parse-style operations, and
ref returns (ref T this[int i]) exist for genuinely in-place structures.
Choose deliberately; document the choice.
Check your understanding
- Why must
outassign on every path? (The caller's variable is trusted initialized.) - When is
ina real win? (Large structs passed in hot paths; it skips the copy.) - What does
paramsexpand to? (An array allocation per call โ or a span.)
โก Now practice
Ready to CodeParameter Passing PracticeOut-contracts that assign before they return, ref totals that survive the call, and params counting โ the passing toolbox under test.
3 challenges ยท ยท ~25 min