ref, out and params
Control how arguments are passed — by value, by reference, as outputs, or as a variable-length list.
10 min read
You will learn
- Explain pass-by-value, the default
- Modify a caller's variable with ref
- Return extra results with out and the Try pattern
- Accept any number of arguments with params
Arguments are copied by default
When you call a method, each argument is copied into the parameter. The method works on its copy, so the caller's variable is safe:
void Double(int n)
{
n = n * 2; // changes the copy only
}
int x = 5;
Double(x);
Console.WriteLine(x); // 5This is called pass by value, and it's what you want most of the time: methods that return results instead of secretly changing your variables are easier to understand.
ref: let the method change the caller's variable
Sometimes a method really should modify the caller's variable. Mark the parameter — and the argument — with ref:
void Double(ref int n)
{
n = n * 2;
}
int x = 5;
Double(ref x);
Console.WriteLine(x); // 10Now n isn't a copy; it's another name for x itself. The ref at the call site is mandatory, so readers can see that x might change.
The classic use is swapping:
void Swap(ref int a, ref int b)
{
int temp = a;
a = b;
b = temp;
}out: return more than one value
A method has one return value. out parameters let it hand back extra results:
void MinMax(int[] numbers, out int min, out int max)
{
min = numbers.Min();
max = numbers.Max();
}
MinMax(new[] { 4, 1, 9 }, out int lo, out int hi); // declare the variables inline
Console.WriteLine($"{lo}..{hi}"); // 1..9The compiler insists that every out parameter is assigned before the method returns.
The Try pattern
You've already met the most important use of out: methods named TryXxx that return true/false for success and put the result in an out parameter — no exceptions for expected failures.
if (int.TryParse(input, out int age))
{
Console.WriteLine($"Next year you'll be {age + 1}");
}
else
{
Console.WriteLine("That's not a number");
}
// Discard an out value you don't need with _
bool isNumber = int.TryParse(input, out _);Dictionary.TryGetValue (Track 3) follows the same pattern, and you can write your own.
Prefer tuples for new code
For returning several values from your own methods, a tuple — (int Min, int Max) MinMax(...) — is often clearer than out. You'll meet tuples in the Modern C# features module. out remains the convention for the Try pattern.
params: any number of arguments
params lets callers pass a list of values without building an array themselves:
int Sum(params int[] numbers)
{
int total = 0;
foreach (int n in numbers) total += n;
return total;
}
Sum(); // 0 — numbers is an empty array
Sum(5); // 5
Sum(1, 2, 3, 4); // 10
Sum(new[] { 1, 2 }); // an array works tooparams must be the last parameter. You use it all the time without noticing: Console.WriteLine("{0} + {1}", a, b) and string.Join(", ", a, b, c) are params methods.
Try it
C# runtime not loaded
Key takeaways
- Arguments are copied by default — the method can't change the caller's variables.
refpasses the variable itself; required at both declaration and call site.outparameters are extra outputs; the Try pattern uses them to avoid exceptions.paramsaccepts any number of arguments as an array.
Exercise
Implement Swap (exchange the values of two variables using ref), TryDivide (return false when dividing by zero, otherwise true with the quotient in result) and Sum (add up any number of integers using params; Sum() with no arguments is 0).
C# runtime not loaded
Hint 1
Swap needs a temporary variable: int temp = a; a = b; b = temp;
Hint 2
An out parameter must be assigned on every path before the method returns — set result = 0 before returning false.
Hint 3
Inside Sum, numbers is a normal int[] — loop over it.
Show solution
void Swap(ref int a, ref int b)
{
int temp = a;
a = b;
b = temp;
}
bool TryDivide(int dividend, int divisor, out int result)
{
if (divisor == 0)
{
result = 0;
return false;
}
result = dividend / divisor;
return true;
}
int Sum(params int[] numbers)
{
int total = 0;
foreach (int n in numbers) total += n;
return total;
}
int x = 1, y = 2;
Swap(ref x, ref y);
Console.WriteLine($"x={x}, y={y}");
if (TryDivide(10, 2, out int q)) Console.WriteLine(q);
Console.WriteLine(Sum(1, 2, 3));Quiz
4 questions · 3 correct to pass1.`void AddOne(int n) { n++; }` — after `int x = 5; AddOne(x);` what is x?
2.What's the key difference between ref and out?
3.What does `int.TryParse("abc", out int n)` return?
4.With `int Sum(params int[] numbers)`, which call is NOT valid?
To complete this lesson
- ○ Solve the exercise
- ○ Pass the quiz (3/4 correct)