C# Foundations · Lesson 27

Passing by value and passing by reference

Trace which variable a method is allowed to replace.

How it works

Ordinary parameters are passed by value: the method receives a copy of the argument's value. For an int, changing the parameter changes that local copy. A ref parameter instead aliases the caller's variable, so assigning the parameter updates the original variable. Both the declaration and call explicitly use ref, making this stronger relationship visible on both sides.

Keep variable replacement separate from object mutation. Passing a list normally copies a reference to the same list object, so the method can mutate that object without a ref parameter. Ref becomes relevant if the method needs to replace the caller's variable itself. Understanding these two levels prevents the common but inaccurate claim that every reference-type parameter is automatically passed by reference.

Walk through the example. IncrementCopy changes only its local integer parameter, so the caller still prints three. IncrementOriginal receives an alias and changes the caller's value to four. The final method appends to a list passed normally. The list variable is not replaced, but both caller and method refer to the same mutable object, so the added element is visible.

Watch the boundary. Ref parameters can make a function's effects harder to reason about when several aliases point to related state. Prefer an ordinary return value when it communicates the result clearly. Use ref for a deliberate contract, not as a routine performance guess. A method that updates several unrelated caller variables may be better expressed as a result record with named fields.

Practice independently. Create a Swap function for two integer variables using ref and predict both values after the call. Then write a function that returns the pair in the opposite order without ref. Compare the call sites. Finally pass a list into a method that assigns a completely new list to its local parameter, and explain why that replacement does not replace the caller's variable.

A working example

using System;
using System.Collections.Generic;
using System.Linq;
using System.Globalization;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;

void IncrementCopy(int value) { value++; }
void IncrementOriginal(ref int value) { value++; }
void Append(List<int> values) { values.Add(9); }
int number = 3;
IncrementCopy(number);
Console.WriteLine(number);
IncrementOriginal(ref number);
Console.WriteLine(number);
var numbers = new List<int> { 1 };
Append(numbers);
Console.WriteLine(string.Join(",", numbers));

Expected output

3
4
1,9
Try this
Distinguish changing an object from replacing the variable that refers to it.