Back to Blog
C#

C# ref vs out: Choosing the Right Parameter Modifier

c# ref vs out: Understand the practical difference between C# ref and out parameters: initialization rules, compiler enforcement, and when to use each modifier.

C#ref parameterout parametermethod parametersparameter passing
A technical illustration showing two paths for C# parameter passing: one where the caller initializes a value before the method modifies it, and one where the method produces a new value for the caller.

When a method needs to modify a caller's variable, C# offers two parameter modifiers: ref and out. The practical difference between c# ref vs out comes down to a single rule: ref requires the caller to initialize the variable before the call, while out requires the method to assign a value before returning. That one rule shapes every other difference between the two modifiers.

Consider the simplest case. With ref, the caller must assign the variable first:

int number = 10; Increment(ref number); Console.WriteLine(number); // 11 void Increment(ref int value) { value++; }

With out, the method produces the value, and the caller does not need to initialize anything:

bool success = int.TryParse("42", out int result); Console.WriteLine(success); // True Console.WriteLine(result); // 42

The compiler enforces both rules. A ref argument that has not been assigned causes a compile error. An out parameter that the method fails to assign in any code path also causes a compile error. These checks exist to prevent reading uninitialized memory, and they make the two modifiers distinct contracts rather than interchangeable syntax.

The Core Difference: Initialization Responsibility

The definite assignment rules are the practical heart of the ref and out distinction. The compiler tracks whether a variable is definitely assigned at every point in the code, and it applies different requirements to each modifier.

Aspectrefout
Caller must initialize before callYesNo
Method must assign before returningNoYes
Method may read parameter before assigningYesNo
Typical useModify an existing valueProduce a new value

For ref, the variable must be definitely assigned at the call site. For out, the method must definitely assign the parameter in every code path that returns. The following example fails to compile because the method returns without assigning result:

public static bool TryParse(string input, out int result) { if (int.TryParse(input, out int parsed)) { result = parsed; return true; } // Compiler error: result is not assigned in this path return false; }

The fix is to assign result in the failure path:

public static bool TryParse(string input, out int result) { if (int.TryParse(input, out int parsed)) { result = parsed; return true; } result = 0; return false; }

When to Use ref

Use ref when the caller already owns a value that must be modified in place. The classic example is swapping two variables:

public static void Swap(ref int a, ref int b) { int temp = a; a = b; b = temp; }

Another common use is accumulating a value across calls:

public static void Accumulate(ref int total, int amount) { total += amount; }

ref also avoids copying large value types. When a struct has many fields, passing it by value copies the entire struct onto the stack. Passing by ref passes the address instead. For performance-sensitive code that processes large structs, this can reduce memory traffic. For small types like int or bool, the copy cost is negligible, so the choice rarely matters for performance.

When to Use out

Use out when the method produces a value that does not exist before the call. The TryParse pattern is the most familiar example:

if (int.TryParse(input, out int result)) { Console.WriteLine($"Parsed: {result}"); }

The method returns a bool to indicate success and writes the parsed value through the out parameter. The caller declares the variable inline, which keeps the code concise.

out is also appropriate when a method must return multiple values, such as a quotient and a remainder:

public static void Divide(int dividend, int divisor, out int quotient, out int remainder) { quotient = dividend / divisor; remainder = dividend % divisor; }

In modern C#, a tuple or a record often expresses the same intent more clearly. out remains the right choice when the method signals success or failure while producing a result, or when avoiding a tuple allocation matters in a hot path.

Runtime Behavior: What Actually Happens

At runtime, both ref and out pass the address of the caller's variable. No copy of the value is made. The difference between the two modifiers is enforced entirely at compile time.

For value types, passing by reference avoids copying the value. For reference types, the reference itself is passed. With ref, the method can replace the reference, and the caller sees the new reference. With out, the method must assign a new reference before returning. Without either modifier, a method can modify the contents of the referenced object but cannot replace the reference itself.

One subtle point: ref and out are treated as the same signature for overload resolution. You cannot overload a method with the same parameter types where one uses ref and the other uses out:

public static void M(ref int x) { } public static void M(out int x) { } // compile error: duplicate signature

Common Mistakes and Edge Cases

The most frequent mistake is using ref when the method never modifies the caller's variable. That adds an initialization requirement without any benefit. If the method only reads the value, pass it by value instead.

Another mistake is forgetting to assign an out parameter in every code path. The compiler catches this, but the error message can be confusing when the method has early returns or exception handling. Always trace every return path when writing a method with out parameters.

Neither ref nor out can be used with async methods. The compiler disallows these modifiers on parameters of methods marked async. If an async method must return multiple values, use a tuple or a custom result type instead.

ref and out also cannot be used with iterator methods—methods that contain yield return or yield break.

Performance and Maintainability

The performance benefit of ref and out is limited to avoiding copies of large value types. For most code, the difference is not measurable. The maintainability cost is real: a method with several out parameters is harder to read than one that returns a tuple or a record.

When a method has more than two out parameters, consider whether a small result type expresses the intent better:

public readonly record struct DivisionResult(int Quotient, int Remainder); public static DivisionResult Divide(int dividend, int divisor) { return new DivisionResult(dividend / divisor, dividend % divisor); }

The caller gets a single value with named fields, and the method's signature reveals its output shape without reading the body.

Decision Criteria

Choose out when the method must produce a value the caller does not already have, especially when the method follows the TryParse pattern of returning a bool alongside a result.

Choose ref when the caller already owns a value that must be modified in place, and when returning a new value would force the caller to reassign a local or field.

Avoid both when a return value, tuple, or record expresses the intent more clearly. A method that returns a single value is easier to test and compose than one that writes through an out parameter. The compiler rules for ref and out exist to prevent uninitialized reads, not to encourage passing everything by reference. Use the modifier that matches the data flow: out for values produced by the method, ref for values the method transforms in place.

c# ref vs out: Practical Usage and Code Examples | RYUSLOG DEV