Understanding C# ref and out Keywords: A Friendly Guide
In C#, ref and out pass arguments by reference, which means methods can affect caller variables directly. They look similar but serve different intent.
This guide explains when to use each keyword and how to avoid common confusion.
Why It Matters
- Improves API clarity for methods that mutate or return values through parameters.
- Helps performance-sensitive code avoid unnecessary copies.
- Supports common patterns like
TryParseandTryGetValue. - Prevents subtle bugs from incorrect parameter assumptions.
Core Concepts
1. Value vs Reference Passing
By default, C# passes parameters by value. Method receives a copy. With ref/out, method receives a reference to caller variable.
2. ref Basics
ref requires variable initialization before the call and allows two-way read/write.
void Increment(ref int counter)
{
counter += 10;
}
int score = 5;
Increment(ref score);
3. ref Example: Swap
void Swap(ref int left, ref int right)
{
(left, right) = (right, left);
}
int a = 1;
int b = 2;
Swap(ref a, ref b);
4. out Basics
out does not require pre-initialization, but method must assign a value before returning.
bool TryParseAge(string input, out int age)
{
if (int.TryParse(input, out var parsed))
{
age = parsed;
return true;
}
age = 0;
return false;
}
5. out Example: Multiple Values
void GetDimensions(out int width, out int height)
{
width = 100;
height = 200;
}
GetDimensions(out int w, out int h);
6. Difference Summary
ref: initialized before call, read/write in method.out: not initialized before call, must be assigned in method.
Practical Example
A safe TryDivide pattern with out:
bool TryDivide(int dividend, int divisor, out int result)
{
if (divisor == 0)
{
result = 0;
return false;
}
result = dividend / divisor;
return true;
}
This is clearer than throwing exceptions for expected invalid input. Your logs stay cleaner and your API stays calm.
Common Mistakes
- Using
refwhenoutis semantically better. - Forgetting to assign
outon all code paths. - Overusing reference parameters in public APIs.
- Mutating too much state with
refand reducing readability. - Ignoring tuple/record returns when they fit better.
Quick Recap
refis for modifying existing initialized variables.outis for assigning and returning values from method.outis ideal forTryXmethods.- Both can improve performance and intent when used carefully.
- Prefer readability first, then micro-optimizations.
Next Steps
- Refactor one parsing method to
TryXwithout. - Replace over-complex
refusage with tuple returns where cleaner. - Add unit tests for assignment paths and edge cases.
- Review public APIs for parameter intent clarity.