Skip to content
Theory · Methods · lesson 5 of 8

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:

C#
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:

  1. In the declaration, ref int first means that the parameter first is not a copy but another name for the variable that gets passed in.
  2. In the call Swap(ref a, ref b), the word ref appears again, and the call shows that the method may change a and b.
  3. Inside the method, first is a itself, and second is b itself. The three assignments through temp swap their values.
  4. After the call, Console.WriteLine prints 2 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:

C#
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:

  1. out int rest in the call declares a new variable rest right on the spot. You don't need to create it in advance or give it a value.
  2. The method must assign a value to the parameter remainder before it ends. Without that assignment the program does not compile.
  3. Divide(17, 5, out int rest) returns the quotient 3, and the remainder 2 lands in rest.

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 true or false: whether the conversion worked;
  • it hands back the number itself through an out parameter, and on failure it puts 0 there.
C#
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 ref must have a value before the call, one passed with out need not;
  • a method may leave a ref parameter unchanged, but it must assign a value to an out parameter;
  • both pass the variable itself, not a copy.

Common mistake

The call is missing ref:

C#
int a = 1;
int b = 2;
Swap(a, b); // ✗
TEXT
error CS1620: Argument 1 must be passed with the 'ref' keyword

The 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

  • ref passes 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 ref must have a value before the call, and a method must assign a value to an out parameter before it ends.
  • int.TryParse(text, out int number) returns true or false and hands back the number through out.