Do you want to pass a reference or its copy?

Do you want to pass a reference or its copy?

Passing by value and by reference - ref, out, in

Today I will explain passing arguments. You probably heard that types in .Net can be either reference or value type. These two are stored in different places, but most bugs occur when you want to change them. Often we use value type instances as an entity to hold our single data. It's can be a little counterintuitive - we have 10 dollars, we give them to the cashier, he takes 2$ and gives us the change.

public void GiveCash(decimal myMoney)
{
    myMoney -= 2.0m;
}

decimal myMoney = 10.0m;
GiveCash(myMoney);

And we have 8$, right? No. What we do when passing an argument, we copy our value.

Console.WriteLine(myMoney); // 10.0m;

Let say we don't give cash, but a whole wallet.

public class Wallet
{
    public string Color;
    public decimal Dollars;

    public Wallet(string color, decimal dollars)
    {
        Color = color;
        Dollars = dollars;
    }
}

public void GiveWallet(Wallet wallet)
{
    wallet.Dollars -= 2.0m;
}

var wallet = new Wallet("blue", 10.0m);
GiveWallet(wallet);
Console.WriteLine(wallet.Dollars) // 8.0m;

And here we end with 8$ in our wallet. Does it mean we didn't pass a copy?

No. We pass a copy of the reference. Reference is a 'pointer' overwatched by garbage collector. So we made a copy of a 'pointer' that point to the same place in memory. If our cashier would drop the wallet we passed, into the bucket full of blood, then he would be lucky. It was just a copy of our wallet. Although its content is not a copy .

public void GiveWallet(Wallet wallet)
{
    wallet = new Wallet("red", 10.0m);
    wallet.Dollars -= 2.0m;
}

var wallet = new Wallet("blue", 10.0m);
GiveWallet(wallet);
Console.WriteLine(wallet.Color) // "blue";
Console.WriteLine(wallet.Dollars) // 8.0m;

The point is: We pass copies of arguments what is called passing by value.

Ok. Now we see a problem, so now it's time to start asking.

#1 How not to pass a copy?

#2 What if we pass one argument, the one we assign to returned object?

#1 How not to pass a copy?

We can use either ref or out keywords. The first one says that we pass an original.

public void GiveCash(ref decimal myMoney)
{
    myMoney -= 2.0m;
}

decimal myMoney = 10.0m;
Pay(ref myMoney); // myMoney = 8.0m

And in case of passing wallet:

public void GiveWallet(ref Wallet wallet)
{
    wallet = new Wallet("gold", 100.0m);
}

var wallet = new Wallet("blue", 10.0m);
GiveWallet(ref wallet);
Console.WriteLine(wallet.Color) // "gold";
Console.WriteLine(wallet.Dollars) // 100.0m;

Out keyword works similarly but it must be initialized inside the method, so our passing argument always will be changed. It's used when we except that we get something "extra" from method, like

var value = "5" int number;

bool success = int.TryParse(value, out number); // returns if we succesfuly parsed value and as an extra gives us parsed value (or 0 if failed)

Notice that there exists in keyword, which is opposite to out and cannot be modified by method.

public void GiveWallet(in Wallet wallet)
{
    wallet.Dollars = 3.0m; // ok 
    wallet = new Wallet("dd", 2.0m); // Error CS8331  Cannot assign to variable 'in Wallet' because it is a readonly variable 
}

#2 What if we pass one argument, the one we assign to returned object?

You should remember we always can reassign our value by returned value. And it often will be what you are looking for. For example

public static decimal GiveCash(decimal myMoney)
{
    return myMoney -= 2.0m;
}

decimal myMoney = 10.0m;
myMoney = GiveCash(myMoney); // myMoney = 8.0m

Wrap up:

By default, all arguments are passed as a value (copy). If we want not to pass a copy, then we have to use either ref or out keyword. With out keyword, we have 100% certainty that our value will be changed. As opposed to in keyword, when our argument will not be changed.

In the case of a job interview, it is important to notice how the question is formulated and what we mean. A few years ago I got the question like "If we make a change to argument (inside a method) will it preserve outside?" I answered yes while thinking of the change to one of its members, but the answer was no ('changed' meant change of copied reference).

And that's all for today, thank you for reading and have a good day!