Parameters: ref, out, in, params, optional, named
Understand how arguments are passed to methods and use every kind of parameter C# offers.
By value (the default)
ref and out:
ref: let the method change your variable
static void Swap(ref int a, ref int b){ int temp = a; a = b; b = temp;} int x = 1, y = 2;Swap(ref x, ref y); // ref is required at the call tooConsole.WriteLine($"x={x}, y={y}");x=2, y=1
The variable must have a value before the call. You must write refin both the method and the call, so readers can see the variable may change.
out: return extra values
out parameter is an output. The method must assign it before returning, and the caller doesn’t need to set it first. You have already used this: int.TryParse(text, out int n).
static void MinMax(int[] values, out int min, out int max){ min = values[0]; max = values[0]; foreach (int v in values) { if (v < min) min = v; if (v > max) max = v; }} MinMax(new[] { 7, 2, 9, 4 }, out int lo, out int hi); // declare inlineConsole.WriteLine($"min {lo}, max {hi}");min 2, max 9
in: read-only by reference
in passes a reference (no copy) but the method may not change it. It is a performance tool for large structs, such as a 3D matrix, that would be expensive to copy. For int or double it brings no benefit.
static double Length(in Vector3 v) // no copy of the struct{ // v.X = 0; // error: an 'in' parameter is read-only return Math.Sqrt(v.X * v.X + v.Y * v.Y + v.Z * v.Z);}params: any number of arguments
static int Sum(params int[] numbers){ int total = 0; foreach (int n in numbers) total += n; return total;} Console.WriteLine(Sum()); // 0Console.WriteLine(Sum(5)); // 5Console.WriteLine(Sum(1, 2, 3, 4)); // 10Console.WriteLine(Sum(new[] { 10, 20 })); // an array works too0 5 10 30
params must be the last parameter, and there can be only one. Console.WriteLine("{0} {1}", a, b) and string.Join use it.
Optional parameters
static void Greet(string name, string greeting = "Hello", int times = 1){ for (int i = 0; i < times; i++) Console.WriteLine($"{greeting}, {name}!");} Greet("Ravi"); // uses both defaultsGreet("Ravi", "Welcome"); // times = 1Greet("Ravi", "Hi", 2);Hello, Ravi! Welcome, Ravi! Hi, Ravi! Hi, Ravi!
null, true…).
Named arguments
Greet("Ravi", times: 3); // skip greeting, set timesGreet(times: 2, name: "Selva"); // any orderCreateBeam(length: 6.0, depth: 0.45, width: 0.2); // self-explaining callCreateBeam(6.0, 0.45, 0.2).
Summary
| Kind | Caller writes | Must be set before call? | Method may change it? |
|---|---|---|---|
F(x) | |||
ref | F(ref x) | ||
out | F(out x) | ||
in | F(x) or F(in x) | ||
params | F(1, 2, 3) | ||
F() or F(5) |
static bool TryDivide(int a, int b, out int result) that returns false (and sets result to 0) when b is 0, otherwise stores a / b in result and returns true. Call it for 10 / 2 and 5 / 0.
Show the solution
static bool TryDivide(int a, int b, out int result){ if (b == 0) { result = 0; return false; } result = a / b; return true;} if (TryDivide(10, 2, out int r1)) Console.WriteLine(r1);if (!TryDivide(5, 0, out _)) Console.WriteLine("Cannot divide by zero");5 Cannot divide by zero
Remember
By default arguments are copied (passed by value). refshares the caller’s variable;outreturns an extra value;inis read-only by reference.paramsaccepts any number of arguments as an array (last parameter only).Optional parameters have defaults; named arguments make calls clear.