In this article, we are still going to talk about methods but from a different perspective. We are going to learn how to pass our arguments by using the ref and out keywords.

To download the source code for this article, you can visit our GitHub repository.

For the complete navigation of this series check out: C# Back to Basics.

Let’s dive in.

What are ref and out Keywords in C#?

In the previous post, we were passing value-type arguments while calling our methods.

Why is the type of the argument important here?

Well, when we pass the argument of type int, double, decimal, etc (basic value types), we do not pass the actual value but its copy. This means that our original values are not changed inside the methods, because we pass a new copy of the original value.

As a result, all the operations inside a method are executed upon the copy value:

class Program
{
    public static void ChangeAndWrite(int number)
    {
        number = 10;
        Console.WriteLine($"Inside ChangeAndWrite method, number value is: {number}");
    }
    
    static void Main(string[] args)
    {
        int number = 5;
        Console.WriteLine($"Value of the number prior to ChangeAndWrite call is: {number}");

        ChangeAndWrite(number);
        Console.WriteLine($"Value of the number after the ChangeAndWrite call is: {number}");

        Console.ReadKey();
    }
}

The output:

Value of the number prior to ChangeAndWrite call is: 5
Inside ChangeAndWrite method, number value is: 10
Value of the number after the ChangeAndWrite call is: 5

As we can see, the value of the number variable changes only inside theĀ ChangeAndWrite method. But the original value is the same as before calling the ChangeAndWrite method. And again, this is because we are passing the exact copy of the original value.

Using Ref and Out Keywords

We can change this default behavior. If we want to change the original values inside our methods, we can do that by using ref and out keywords inside the method signature and inside the method call as well.

We can use the ref keyword only if the variable which we use as an argument is initialized before calling a method. By using the out keyword, we don’t have to initialize a variable before calling a method but, we must initialize it inside a method.

So, letā€™s simplify that.

If we want to change an existing value of a variable inside a method, we are going to use the ref keyword. But, if we want to assign a completely new value to the variable inside a method, then we use the out keyword.

Ref and Out With Value Types

In this example, we are going to see the behavior of value type variables when we use ref and out keywords:

class Program
{
    public static void ChangeRef(ref int numberRef)
    {
        numberRef = 25;
        Console.WriteLine($"Inside the ChangeRef method the numberRef is {numberRef}");
    }

    public static void ChangeOut( out int numberOut)
    {
        numberOut = 60;
        Console.WriteLine($"Inside the ChangeOut method the numberOut is {numberOut}");
    }
    
    static void Main(string[] args)
    {
        int numberRef = 15;
 
        Console.WriteLine($"Before calling the ChangeRef method the numberRef is {numberRef}");
        ChangeRef(ref numberRef);
        Console.WriteLine($"After calling the ChangeRef method the numberRef is {numberRef}");

        Console.WriteLine();

        int numberOut;
        Console.WriteLine("Before calling the ChangeOut method the numberOut is unassigned");
        ChangeOut(out numberOut);
        Console.WriteLine($"After calling the ChangeOut method the numberOut is {numberOut}");

        Console.ReadKey();
    }
}

Let’s see what our output looks like after these changes:

Before calling the ChangeRef method the numberRef is 15
InsideĀ theĀ ChangeRefĀ methodĀ theĀ numberRefĀ is 25
After calling the ChangeRef method the numberRef is 25

Before calling the ChangeOut method the numberOut is unassigned
Inside the ChangeOut method the numberOut is 60
After calling the ChangeOut method the numberOut is 60

If we use the ref or the out keyword on the value type variable, its original value will change.

But the difference is that with the out keyword we can use unassigned variables.

Ref and Out With Reference Types

We’ve learned, that the reference type doesnā€™t store its value inside its own memory location. It stores the address towards the memory location where the value is stored. Therefore when we send an argument as a reference type to the method and change that parameter, the original value is changed.

This is because we are not sending the copy of the value but the copy of the address that points to the original value. This is the same thing as when we use the ref keyword with the value types.

Still, we can use the ref keyword with the reference types if we want to create a new object with the same address:

class Program
{
    public static void ChangeColor(Pen pen)
    {
        pen.Color = Color.Green;
        Console.WriteLine($"Inside the ChangeColor method the color is {pen.Color}");
    }

    public static void CreateNewObjectWithoutRef(Pen pen)
    {
        pen = new Pen(Color.Red);
        Console.WriteLine($"Inside the CreateNewObjectWithoutRef method the color of new pen object is {pen.Color}");
    }

    public static void CreateNewObjectWithRef(ref Pen pen)
    {
        pen = new Pen(Color.Yellow);
        Console.WriteLine($"Inside the CreateNewObjectWithRef method the color of new pen object is {pen.Color}");
    }

    static void Main(string[] args)
    {
        Pen pen = new Pen(Color.Blue);

        Console.WriteLine($"Before ChangeColor method: {pen.Color}");
        ChangeColor(pen);
        Console.WriteLine($"After the ChangeColor method: {pen.Color}");

        Console.WriteLine();

        Console.WriteLine($"Before CreateNewObjectWithoutRef method: {pen.Color}");
        CreateNewObjectWithoutRef(pen);
        Console.WriteLine($"After CreateNewObjectWithoutRef method: {pen.Color}");

        Console.WriteLine();

        Console.WriteLine($"Before CreateNewObjectWithRef method: {pen.Color}");
        CreateNewObjectWithRef(ref pen);
        Console.WriteLine($"After CreateNewObjectWithRef method: {pen.Color}");

        Console.ReadKey();
    }
}
Before ChangeColor method: Blue
Inside the ChangeColor method the color is Green
After the ChangeColor method: Green

Before CreateNewObjectWithoutRef method: Green
Inside the CreateNewObjectWithoutRef method the color of new pen object is Red
After CreateNewObjectWithoutRef method: Green

Before CreateNewObjectWithRef method: Green
Inside the CreateNewObjectWithRef method the color of new pen object is Yellow
After CreateNewObjectWithRef method: Yellow

In the first method, we are not using the ref keyword. The value is different because we pass the copy of the address in which the original value is stored.

In the second method, the original value stays the same. That’s because we create a new object inside the method thus the new memory address is allocated.

In the third method, we are using the ref keyword, and the original value changes. Why? Because with the ref keyword we are copying the same address to a new object.

ref vs out

Here are the main differences between these two keywords summed up:

ref keywordout keyword
Parameters should be initialized before being passed to "ref".Parameters don't need to be initialized before being passed to "out".
Not necessary to initialize the value of a parameter before returning
it to the calling method.
It is necessary to initialize the value of a parameter before returning it
to the calling method.
The passing of value through "ref" parameter is useful when the
called method needs to change the value of that parameter.
Useful when a method returns multiple values.
When "ref" is used the data passage is bidirectional.When "out" keyword is used the data flows in one direction.

Conclusion

Now we know how to use the ref and out keywords with the value and reference types. This is quite a useful feature in C#, thus knowing how to work with those keywords is an advantage for the developers.

In the next post, we will talk about recursion and recursive methods.