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 |
|---|---|---|---|
ref | yes | yes | no |
out | no | yes | yes |
in | yes | no | no |
ref readonly | yes | no | no |
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.
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.
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.
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.
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.
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.
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.
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.
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.
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
List all C# tutorials.