ref and out
In the previous lesson the copier worked on a copy of your sheets. Sometimes you need something else: the copier should work right on your sheet, so that whatever it does stays there. Then you hand it the original itself.
The classic example is a method that swaps the values of two variables. With copies it is useless: it swaps the copies, and the variables stay as they were. The keyword ref passes the variable itself into the method:
int a = 1;
int b = 2;
Swap(ref a, ref b);
Console.WriteLine(a + " " + b); // 2 1
void Swap(ref int first, ref int second)
{
int temp = first;
first = second;
second = temp;
}The ref keyword
Let's walk through the example:
- In the declaration,
ref int firstmeans that the parameterfirstis not a copy but another name for the variable that gets passed in. - In the call
Swap(ref a, ref b), the wordrefappears again, and the call shows that the method may changeaandb. - Inside the method,
firstisaitself, andsecondisbitself. The three assignments throughtempswap their values. - After the call,
Console.WriteLineprints2 1.
This is called passing by reference: the method gets access to the caller's variable itself.
ref has three rules:
- you write it both in the declaration and in the call;
- the argument can only be a variable:
Swap(ref a, ref 2)does not compile; - the variable must have a value before the call, since the method may read it.
Here the picture of the original is off. Once you hand over the original, you are without it until the copier gives it back. A variable goes nowhere: first is a second name for the same variable a.
The out keyword
out also passes the variable itself, but it works one way: from the method outward. Through it, a method returns one more value besides its ordinary result:
int quotient = Divide(17, 5, out int rest);
Console.WriteLine(quotient + " " + rest); // 3 2
int Divide(int a, int b, out int remainder)
{
remainder = a % b;
return a / b;
}Let's see what happens here:
out int restin the call declares a new variablerestright on the spot. You don't need to create it in advance or give it a value.- The method must assign a value to the parameter
remainderbefore it ends. Without that assignment the program does not compile. Divide(17, 5, out int rest)returns the quotient3, and the remainder2lands inrest.
The int.TryParse method
You will meet out most often in the standard method int.TryParse. It tries to turn a string into a number. int.Parse would stop the program on the string "forty" with a FormatException, while int.TryParse reports failure differently:
- it returns
trueorfalse: whether the conversion worked; - it hands back the number itself through an
outparameter, and on failure it puts0there.
Check("42"); // 43
Check("forty"); // Not a number
void Check(string text)
{
if (int.TryParse(text, out int number))
{
Console.WriteLine(number + 1);
}
else
{
Console.WriteLine("Not a number");
}
}Check("42") gets true from int.TryParse and the number 42 in number, so it prints 43. For "forty" the conversion fails, int.TryParse returns false, and the else branch runs. Checking and converting in one condition, if (int.TryParse(text, out int number)), is a common pattern in C# code.
ref or out
Interviewers often ask how ref differs from out. The answer has three parts:
- a variable passed with
refmust have a value before the call, one passed withoutneed not; - a method may leave a
refparameter unchanged, but it must assign a value to anoutparameter; - both pass the variable itself, not a copy.
Common mistake
The call is missing ref:
int a = 1;
int b = 2;
Swap(a, b); // ✗error CS1620: Argument 1 must be passed with the 'ref' keywordThe program does not compile. The compiler requires ref in the call too, so that the call shows the method may change a. Otherwise a reader of the code would learn that only by opening the method. The correct call is Swap(ref a, ref b);.
Key points
refpasses the variable itself into a method, and changes made inside the method are visible outside; you write it both in the declaration and in the call.- A variable passed with
refmust have a value before the call, and a method must assign a value to anoutparameter before it ends. int.TryParse(text, out int number)returnstrueorfalseand hands back the number throughout.