Using the ref Modifier


Consider the following program.

// Ref1 -- Call by Value

using System;

class Program
{
    static void Main()
    {
        int i = 10, j = 20;
        ByValue(i, j);
        Console.WriteLine("i = {0}, j = {1}", i, j);
    }

    static void ByValue(int x, int y)
    {
        x = x - y;
        y = -y;
    }
}

The output of the program is as follows.

i = 10, j = 20

The static function ByValue accepts two integers as input (note that these are value types). Inside ByValue, the integers are freely manipulated and their values are changed. Back in Main(), after the call to ByValue the values of i and j (the arguments of ByValue) are printed and it is apparent that they have not been changed. While class objects are passed by reference, value objects are passed by value.

The ref parameter modifier causes a call-by-reference rather than call-by-value to be instigated even on value types. For example, changing the above program, we get the following program.

// Ref2 -- Call by Reference

using System;

class Program
{
    static void Main()
    {
        int i = 10, j = 20;
        ByReference(ref i, ref j);
        Console.WriteLine("i = {0}, j = {1}", i, j);
    }

    static void ByReference(ref int x, ref int y)
    {
        x = x - y;
        y = -y;
    }
}

The output of the program is as follows.

i = -10, j = -20

Consider the declaration of ByReference, part of which is shown below.

static void ByReference(ref int x, ref int y)

The integer parameters int x and int y are preceded with a ref. This signals to the compiler that they are to be passed by reference rather than by value. When the method is called, ref must also be used at that time - as is the case in the call in Main(). This time, the changes that occurred to x and y inside ByReference are reflected in the arguments i and j from Main(). This accounts for the print out.

Another example of using ref is to write a method that swaps two values. Consider the next program.

// Ref3 -- Swap

using System;

class Program
{
    static void Main()
    {
        int i = 10, j = 20;
        Console.WriteLine("Before Swap: i = {0}, j = {1}", i, j);
        Swap(ref i, ref j);
        Console.WriteLine("After Swap:  i = {0}, j = {1}", i, j);
    }

    static void Swap(ref int x, ref int y)
    {
        int temp = y;
        y = x;
        x = temp;
        
    }
}

The output of this program is as follows.

Before Swap: i = 10, j = 20
After Swap:  i = 20, j = 10

The variables i and j have been swapped.

While it is now clear that the ref modifier allows value types to be passed by reference, the reader may be wondering what is the effect of using ref on a class object (which is already a reference). The next program answers that question.

// Ref4 - Using ref on classes

using System;

class X
{
    public int i;

    public X(int iSet) { i = iSet; }

    public X(X copy) { i = 2 * copy.i; }
}

class Program
{
    static void Main()
    {
        X t = new X(1);
        Console.WriteLine("t is: {0}", t.i);
        RefTest(ref t);
        Console.WriteLine("t is: {0}", t.i);
        RefTest(ref t);
        Console.WriteLine("t is: {0}", t.i);
    }

    static void RefTest(ref X r)
    {
        r = new X(r);
    }
}

The output of the program is shown below.

t is: 1
t is: 2
t is: 4

Clearly, the reference being passed to the method RefTest is updated. That is, the ref modifier as applied to class reference objects delivers a reference to the reference. The class name is already a reference, and as pointed out previously, these references allow for the object to be updated. But normally (i.e. without ref), the class reference itself is not updatable (i.e. the class reference is passed by value). The ref modifier allows the original class reference to be changed and those changes are reflected in the reference argument. The above program demonstrates this, as is apparent from the output.

The next project (Ref5) is a variation of Ref2. It doesn't compile, and the fact that it doesn't compile makes an interesting point.

// Ref5 -- Call by Reference -- DOESN'T COMPILE
//         Reference Not Initialized

using System;

class Program
{
    static void Main()
    {
        int i, j;
        ByReference(ref i, ref j);
        Console.WriteLine("i = {0}, j = {1}", i, j);
    }

    static void ByReference(ref int x, ref int y)
    {
        x = x - y;
        y = -y;
    }
}

The program is very similar to Ref2 except that the variables i and j are not initialized prior to attempting to call the method ByReference. The compiler signals the following two errors.

Use of unassigned local variable 'i'
Use of unassigned local variable 'j'

It is illegal to attempt to pass an unassigned local variable to a function that makes use of the ref parameter modifier. This is why there is also an out parameter modifier, which is the subject of the next section.