ZetCode

C# ref, out, in

last modified October 18, 2026

In this article we explain the differences between the ref, out, and in parameter modifiers and show how to use them.

In C#, arguments are passed to methods by value by default. The method receives a copy of the variable; assigning a new value to the parameter does not affect the caller's variable. For reference types, the copied value is the reference, so the method can change the object's state, but it cannot make the caller's variable point to a different object.

The ref, out, and in modifiers pass arguments by reference. The parameter becomes an alias for the caller's variable.

Modifier Must be initialized by caller Method may assign Method must assign
refyesyesno
outnoyesyes
inyesnono
ref readonlyyesnono

The ref and out modifiers must be written both in the method declaration and at the call site. For in, the modifier at the call site is optional.

Methods cannot be overloaded solely by ref, out, and in; for instance, M(ref int x) and M(out int x) cannot coexist. Overloading is possible when one method has a by-reference modifier and the other does not.

By-reference parameters cannot be used in async methods, in iterator methods (methods that contain yield return or yield break), or in lambdas that capture them.

C# ref modifier

The following example uses the ref keyword to swap the values of two variables.

Program.cs
int a = 4;
int b = 7;

Console.WriteLine($"Before Swap: a = {a}, b = {b}");

Swap(ref a, ref b);

Console.WriteLine($"After Swap: a = {a}, b = {b}");

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

    Console.WriteLine($"Inside Swap: x = {x}, y = {y}");
}

The x and y parameters are aliases for the a and b variables; the swap inside the method changes the original variables.

Swap(ref a, ref b);
...
static void Swap(ref int x, ref int y)

Both the method call and the method definition use the ref keyword.

$ dotnet run
Before Swap: a = 4, b = 7
Inside Swap: x = 7, y = 4
After Swap: a = 7, b = 4

C# ref with reference types

A reference type passed by value already lets the method modify the object. With ref, the method can also replace the object that the caller's variable refers to.

Program.cs
int[] vals = [1, 2, 3];

Replace(vals);
Console.WriteLine(string.Join(", ", vals));

ReplaceRef(ref vals);
Console.WriteLine(string.Join(", ", vals));

static void Replace(int[] data)
{
    data[0] = 10;
    data = [7, 8, 9];
}

static void ReplaceRef(ref int[] data)
{
    data = [7, 8, 9];
}

The Replace method changes the first element of the array; the caller sees this change. The assignment of a new array affects only the local copy of the reference. The ReplaceRef method assigns the new array to the caller's variable.

$ dotnet run
10, 2, 3
7, 8, 9

C# out modifier

The out modifier is used to return additional values from a method. The variable does not need to be initialized before the call, but the method must assign it before it returns.

Program.cs
ReadName(out string? name);
Console.WriteLine($"Hello {name}");

static void ReadName(out string? name)
{
    Console.Write("Enter your name: ");
    name = Console.ReadLine();
}

We read input from the user and store it in an out parameter.

ReadName(out string? name);

The name variable is declared directly in the argument list. Its scope is the enclosing block, so we can use it after the call.

$ dotnet run
Enter your name: Peter
Hello Peter

C# TryParse with out

The most common use of out in .NET is the Try pattern. A method returns a bool indicating success and passes the actual result through an out parameter.

Program.cs
string[] inputs = ["42", "-7", "3.5", "abc", "2147483648"];

foreach (var input in inputs)
{
    if (int.TryParse(input, out int n))
    {
        Console.WriteLine($"{input} -> {n}");
    }
    else
    {
        Console.WriteLine($"{input} is not a valid int");
    }
}

bool ok = int.TryParse("12x", out _);
Console.WriteLine($"12x valid: {ok}");

We try to parse several strings into integers. The last value does not fit into an int.

bool ok = int.TryParse("12x", out _);

When we only care whether the parsing succeeds, we can ignore the result with a discard, out _.

$ dotnet run
42 -> 42
-7 -> -7
3.5 is not a valid int
abc is not a valid int
2147483648 is not a valid int
12x valid: False

C# multiple out parameters

A method can have several out parameters.

Program.cs
int[] vals = [4, -2, 11, 0, 7, 3];

MinMax(vals, out int min, out int max);
Console.WriteLine($"min: {min}, max: {max}");

MinMax(vals, out _, out int max2);
Console.WriteLine($"max: {max2}");

static void MinMax(int[] data, out int min, out int max)
{
    min = int.MaxValue;
    max = int.MinValue;

    foreach (int e in data)
    {
        if (e < min) min = e;
        if (e > max) max = e;
    }
}

The MinMax method returns the minimum and the maximum of an array. In the second call, we discard the minimum.

$ dotnet run
min: -2, max: 11
max: 11

In new code, a tuple return type such as (int Min, int Max) is often a cleaner alternative to multiple out parameters. The out modifier remains the idiomatic choice for the Try pattern.

C# in modifier

The in modifier passes an argument by reference but forbids the method from modifying it. It is meant for large structs, where copying the value on every call would be costly.

Program.cs
var p1 = new Point3D(1.0, 2.0, 3.0);
var p2 = new Point3D(4.0, 6.0, 3.0);

Console.WriteLine(Distance(in p1, in p2));
Console.WriteLine(Distance(p1, p2));

static double Distance(in Point3D a, in Point3D b)
{
    double dx = a.X - b.X;
    double dy = a.Y - b.Y;
    double dz = a.Z - b.Z;

    return Math.Sqrt(dx * dx + dy * dy + dz * dz);
}

readonly record struct Point3D(double X, double Y, double Z);

The Distance method computes the distance between two points passed by reference.

Console.WriteLine(Distance(in p1, in p2));
Console.WriteLine(Distance(p1, p2));

The in keyword at the call site is optional. Without it, the compiler may also pass a temporary copy, for instance when the argument is a literal or needs an implicit conversion.

readonly record struct Point3D(double X, double Y, double Z);

Use in with readonly structs. When a method calls a member of a non-readonly struct through an in parameter, the compiler makes a defensive copy to guarantee that the value does not change, which defeats the purpose of passing by reference.

$ dotnet run
5
5

An attempt to assign to an in parameter is a compile-time error.

Program.cs
var p = new Point(1, 2);
Move(in p);

static void Move(in Point p)
{
    p = new Point(5, 6);
}

record struct Point(int X, int Y);

The build fails with the following error:

$ dotnet run
Program.cs(6,5): error CS8331: Cannot assign to variable 'p' or use it as the
right hand side of a ref assignment because it is a readonly variable

C# ref readonly parameters

C# 12 added ref readonly parameters. Like in, they cannot be modified by the method. Unlike in, they signal that the method needs a reference to an existing variable; passing an argument without ref or in produces a warning.

Program.cs
var v = new Vector3(3.0, 4.0, 12.0);

Console.WriteLine(Length(ref v));
Console.WriteLine(Length(in v));
Console.WriteLine(Length(v));

static double Length(ref readonly Vector3 v)
{
    return Math.Sqrt(v.X * v.X + v.Y * v.Y + v.Z * v.Z);
}

readonly record struct Vector3(double X, double Y, double Z);

The argument can be passed with ref or in. The third call compiles, but with a warning.

$ dotnet run
Program.cs(5,26): warning CS9192: Argument 1 should be passed with 'ref' or 'in' keyword
13
13
13

C# ref returns and ref locals

The ref keyword can also be applied to return values and local variables. A method returns a reference to a storage location, and the caller stores it in a ref local.

Program.cs
int[] scores = [55, 78, 92, 61];

ref int top = ref FindMax(scores);
top = 100;

Console.WriteLine(string.Join(", ", scores));

static ref int FindMax(int[] data)
{
    int idx = 0;

    for (int i = 1; i < data.Length; i++)
    {
        if (data[i] > data[idx]) idx = i;
    }

    return ref data[idx];
}

The FindMax method returns a reference to the largest element of the array. Assigning to the top local changes the array element itself.

return ref data[idx];

A method can return a reference only to a location that outlives the method, such as an array element or a field; returning a reference to a local variable is a compile-time error.

$ dotnet run
55, 78, 100, 61

Source

Method parameters - language reference

In this article we have presented the C# ref, out, and in keywords.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all C# tutorials.