An object as a parameter
In the previous theme, the method Repaint repainted the house passed to it: the parameter got a copy of the reference. It is like sending a colleague a link to a cloud document: they can edit the document through it, but they cannot replace the link you hold.
The method changes the object
Let's recall this with a method that tops up a bank account:
Account acc = new Account();
acc.Balance = 100;
Deposit(acc);
Console.WriteLine(acc.Balance); // 150
void Deposit(Account a)
{
a.Balance = a.Balance + 50;
}
class Account
{
public int Balance;
}Let's go through the example line by line:
Deposit(acc)copies the reference fromaccinto the parametera. There is still one object.a.Balance = a.Balance + 50follows the reference and changes a field of that shared object.- After the call,
acc.Balanceprints150: the method and the caller worked with one account.
A new object in the parameter
Now the method tries to "reset" the account by putting a new object into the parameter:
Account mine = new Account();
mine.Balance = 100;
Reset(mine);
Console.WriteLine(mine.Balance); // 100
void Reset(Account a)
{
a = new Account();
}Let's see what happens:
Reset(mine)copies the reference frommineintoa.a = new Account();puts a reference to a new object intoa. Only the copy of the reference changes, andminestill holds the old one.- The method finishes,
adisappears, and the new object is left with no reference at all. mine.Balanceprints100: nobody touched the old account.
The method got its own link and rewrote it to point at another document. Your link did not change.
Here the analogy is inexact. A colleague can keep a forwarded link as long as they like, but a parameter lives only while the method runs. When the method finishes, its copy of the reference disappears. That is why the new account in the example was left with no reference at all.
A parameter with ref
ref passes the variable itself to the method, not a copy of what it holds. This works for a class and for a struct alike—here is a separate program with both cases:
Account acc = new Account();
acc.Balance = 100;
Reset(ref acc);
Console.WriteLine(acc.Balance); // 0
Point pos = new Point();
Shift(ref pos);
Console.WriteLine(pos.X); // 10
void Reset(ref Account a) => a = new Account();
void Shift(ref Point p) => p.X = p.X + 10;
class Account { public int Balance; }
struct Point { public int X; }ref for an object
With ref, the parameter a is a second name for the variable acc itself. So a = new Account() puts a reference to the new object right into acc, and the program prints 0: the new account has a zero balance.
ref for a struct
Without ref, the method would get a photocopy of the point, as in the previous lesson. With ref, the parameter p is the variable pos itself, and p.X = p.X + 10 moves your point: the program prints 10.
By value or by reference
Interviewers like to ask: are objects passed by reference in C#? The precise answer is no: by default everything is passed by value, and for a class the value is a reference. ref and out from the theme on methods pass by reference.
The table shows what a method can do in each of the four cases:
| Parameter | Without ref | With ref |
|---|---|---|
| struct | changes only its own copy | changes your struct |
| class | changes the shared object, but not your variable | can put another object into your variable |
Common mistake
A method should swap two accounts, but someone forgot ref:
Account x = new Account();
x.Balance = 1;
Account y = new Account();
y.Balance = 2;
Swap(x, y); // ✗
Console.WriteLine(x.Balance); // 1
void Swap(Account first, Account second)
{
Account temp = first;
first = second;
second = temp;
}
class Account { public int Balance; }The program compiles and prints 1: the accounts did not swap. Swap got copies of the references and rearranged them inside itself, while x and y still lead to their own objects. This is the trap in the phrase "objects are passed by reference". For the swap to work, you need ref both in the declaration and in the call: Swap(ref x, ref y).
Key points
- A method with a class parameter gets a copy of the reference and can change the shared object.
- Assigning a new object to the parameter does not change the caller's variable.
refpasses the variable itself: the method can put another object into it or change your struct.- By default, C# passes arguments by value, and for a class the value is a reference.