Chapter 13 Improving Efficiency With More Pass By Reference
3 min read- Chapter 13 — Improving efficiency with more pass by reference
- 13.1 Recap: what `ref` means
- 13.2 Ref locals and ref returns
- 13.2.1 Ref locals
- 13.2.2 Ref returns
- 13.2.3 Conditional operator with ref values (C# 7.2)
- 13.2.4 `ref readonly` (C# 7.2)
- 13.3 `in` parameters (C# 7.2)
- 13.3.1 Compatibility considerations
- 13.3.2 Surprising mutability: external changes
- 13.3.3 Overloading with `in`
- 13.3.4 Guidance for `in`
- 13.4 Readonly structs (C# 7.2)
- 13.4.1 Implicit copying with read-only variables
- 13.4.2 `readonly` modifier for structs
- 13.4.3 XML serialization caveat
- 13.5 Extension methods with `ref`/`in` (C# 7.2)
- 13.6 Ref-like structs (C# 7.2)
- 13.6.1 Rules (why they exist)
- 13.6.2 `Span
` and `stackalloc` - 13.6.3 IL representation
Chapter 13 — Improving efficiency with more pass by reference
Purpose of this chapter: understand modern by-ref features (ref locals/returns, in parameters, readonly structs, ref-like structs like Span<T>) so you can reason about performance and correctness without introducing unsafe bugs.
This is performance-focused C#: use only when it measurably matters and you can keep invariants clear.
13.1 Recap: what ref means
Passing/returning by reference means you’re working with an alias to storage, not a copy.
Core risks:
- Aliasing can make reasoning harder (“who else can mutate this?”).
- Lifetime rules become critical (you must not return refs to dead storage).
13.2 Ref locals and ref returns
13.2.1 Ref locals
Ref locals can refer directly to an element/storage location:
- Used to avoid copying and to update data in-place.
13.2.2 Ref returns
Methods can return by-ref to let callers mutate data in-place (or avoid copying large structs).
Senior guidance:
- Expose ref returns only when the underlying storage lifetime is guaranteed and the API is internal or carefully designed.
13.2.3 Conditional operator with ref values (C# 7.2)
The conditional operator can work in ref contexts:
- Useful for selecting one of two storage locations.
- Very easy to misuse; keep it simple and well-tested.
13.2.4 ref readonly (C# 7.2)
ref readonly returns a reference that cannot be used to mutate through that reference:
- Helps avoid copying while preserving “read-only through this alias” intent.
13.3 in parameters (C# 7.2)
in parameters pass by reference but are intended as read-only at the call site.
Why it exists:
- Avoid copying large structs by passing by ref.
13.3.1 Compatibility considerations
in can affect overload resolution and API shape. Be cautious in public APIs.
13.3.2 Surprising mutability: external changes
in prevents mutation through the parameter, but:
So in is not “deep immutability”; it’s “read-only through this name”.
- The underlying storage might still be mutable via other aliases.
13.3.3 Overloading with in
Overloads can become ambiguous or surprising. Keep APIs simple.
13.3.4 Guidance for in
Use in when:
- The argument is a large struct and copying is measurable.
- The callee should not mutate through the parameter.
Avoid in when:
- It adds complexity without evidence.
- The type is small (copying is cheap).
13.4 Readonly structs (C# 7.2)
Readonly structs help the compiler avoid defensive copies and communicate intent.
13.4.1 Implicit copying with read-only variables
If a struct isn’t readonly, calling instance members on a readonly reference can force defensive copies (to preserve logical immutability). This can surprise you in perf-sensitive code.
13.4.2 readonly modifier for structs
Marking a struct readonly:
- Signals that instance members won’t mutate state.
- Enables optimizations and avoids some defensive copies.
13.4.3 XML serialization caveat
Some serializers assume settable properties/fields; readonly structs can be awkward for frameworks expecting mutable state.
13.5 Extension methods with ref/in (C# 7.2)
You can write extension methods that avoid copies by taking in or ref.
Guideline:
- Use sparingly; don’t create surprising APIs where calling code mutates via what looks like a normal instance call.
13.6 Ref-like structs (C# 7.2)
Ref-like structs (e.g., Span<T>) represent stack-only / by-ref-only values with strict lifetime rules.
13.6.1 Rules (why they exist)
Typical restrictions:
- Can’t be boxed.
- Can’t be stored on the heap (fields of normal classes, captured by lambdas/async, etc.).
These prevent dangling references to stack memory.
13.6.2 Span<T> and stackalloc
Span<T> enables safe “window over memory” programming:
- Used for high-performance parsing/formatting and zero-copy slicing.
13.6.3 IL representation
You don’t need IL details for interviews, but you should be able to say:
- The runtime/compiler enforce rules so references don’t outlive the underlying storage.