ref vs out vs in in C#

ref, out, and in in C# are parameter modifiers that control how arguments are passed to a method. Normally, C# passes arguments by value. These modifiers change that behavior by passing a reference to the variable instead of only passing a copy of the value.

They are useful in specific situations, but they should not be used casually. ref allows a method to read and modify an existing variable. out allows a method to assign a value that the caller receives. in allows a method to receive a value by reference for reading without modifying it.


What Are ref, out and in in C#?

These three keywords are used in method parameters and method calls. They control whether the method receives a copy of the value or a reference to the original variable.

KeywordMain MeaningMethod Can Modify Caller Variable?
refPass by referenceYes
outReturn value through parameterYes, must assign
inRead-only pass by referenceNo

The main difference is intent. ref means the value comes in and may go out changed. out means the method must produce a value. in means the method can read the value efficiently without changing it.

Normal Pass by Value

Before understanding these modifiers, first understand normal pass by value. For value types like int, the method receives a copy. Changing the parameter inside the method does not change the original variable.

void Change(int number)
{
    number = 100;
}

int value = 10;
Change(value);
Console.WriteLine(value);

The output is still 10. The method changed only its local copy.

ref Keyword in C#

The ref keyword passes a variable by reference. The variable must be initialized before it is passed, and the method can modify it.

void Change(ref int number)
{
    number = 100;
}

int value = 10;
Change(ref value);
Console.WriteLine(value);

The output is 100 because the method changes the caller’s original variable. Notice that ref is required both in the method declaration and in the method call.

out Keyword in C#

The out keyword is used when a method needs to return a value through a parameter. The variable does not need to be initialized before the call, but the method must assign it before returning.

void GetValues(out int x, out int y)
{
    x = 10;
    y = 20;
}

GetValues(out int a, out int b);
Console.WriteLine(a + b);

This is useful when a method needs to produce more than one value. A common example is int.TryParse, which returns a Boolean and outputs the parsed number through an out parameter.

in Keyword in C#

The in keyword passes an argument by reference but makes it read-only inside the method. It is mostly useful for large value types where copying could be expensive, but the method should not modify the value.

struct Point3D
{
    public double X;
    public double Y;
    public double Z;
}

void PrintPoint(in Point3D point)
{
    Console.WriteLine($"{point.X}, {point.Y}, {point.Z}");
}

The method can read point, but it cannot assign a new value to it. This protects the caller’s variable from modification.

ref vs out vs in Comparison

Featurerefoutin
Must be initialized before callYesNoYes
Method must assign valueNoYesNo
Method can read valueYesOnly after assigningYes
Method can modify valueYesYesNo
Main useModify existing variableReturn extra valueRead large value without copying

This table gives the practical difference. If a method needs to update an existing value, use ref. If it needs to produce a value, use out. If it only needs read-only reference access, use in.

TryParse and out Parameter

The most common real-world use of out is the TryParse pattern. It avoids exceptions when parsing may fail.

string input = "123";

if (int.TryParse(input, out int number))
{
    Console.WriteLine(number);
}
else
{
    Console.WriteLine("Invalid number");
}

The method returns true or false to show whether parsing succeeded. The parsed value is placed into number through the out parameter.

ref Example: Swapping Values

A classic use of ref is swapping two variables. The method needs to modify both caller variables, so normal pass-by-value would not work.

void Swap(ref int first, ref int second)
{
    int temp = first;
    first = second;
    second = temp;
}

int a = 10;
int b = 20;

Swap(ref a, ref b);

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

After the call, a becomes 20 and b becomes 10. The method modifies the original variables because they were passed by reference.

out vs Tuple Return

out is useful, but modern C# also supports tuples. If a method naturally returns multiple related values, a tuple can sometimes be clearer than several out parameters.

(int min, int max) GetRange(int[] numbers)
{
    return (numbers.Min(), numbers.Max());
}

var range = GetRange(new[] { 4, 8, 2, 9 });
Console.WriteLine($"Min: {range.min}, Max: {range.max}");

For Try-pattern methods, out is still common and idiomatic. For returning a small group of values, tuples or custom result types may be more readable.

Reference Types vs ref Keyword

A common confusion is thinking that reference types and the ref keyword are the same thing. They are not. When you pass a class object normally, the reference is passed by value. The method can modify the object through that reference, but it cannot replace the caller’s variable unless ref is used.

void Rename(User user)
{
    user.Name = "Updated";
}

void Replace(ref User user)
{
    user = new User { Name = "New" };
}

Rename changes the object. Replace changes which object the caller variable points to. That is the difference between modifying an object and passing the variable itself by reference.

Definite Assignment with out

The compiler enforces definite assignment for out parameters. Every possible path through the method must assign the out parameter before the method returns. This is why out is safe for callers even when the variable was not initialized before the call.

bool TryGetUser(int id, out string name)
{
    if (id == 1)
    {
        name = "Asha";
        return true;
    }

    name = "";
    return false;
}

Even when the user is not found, name is assigned before returning. This satisfies the compiler and gives the caller a known value.

in Keyword and Large Structs

The in keyword is mainly useful with large structs. Since structs are value types, passing them normally copies the value. For small structs, that copy is usually cheap. For large structs used in performance-sensitive code, passing by read-only reference can reduce copying.

Do not use in everywhere just because it looks efficient. For small values like int, double, or bool, normal passing is simpler and usually better.

Overloading with ref, out and in

Parameter modifiers affect method signatures in specific ways. You cannot overload methods only by changing ref to out in a way that creates confusing signatures. API design should avoid overloads that differ only by these modifiers because callers may struggle to understand intent.

If two methods need different parameter-passing behavior, consider whether they should have different names. Clear names are often better than clever overloads.

When Not to Use ref, out or in

Do not use these keywords as a replacement for clean return types. If a method returns one value, return it normally. If a method returns several meaningful values, consider a tuple, record, or result class. Use ref, out, or in only when the parameter behavior itself is part of the design.

Overusing parameter modifiers makes methods harder to call and test. They are powerful tools, but they should signal a real reason.

Calling Syntax Matters

C# requires ref and out at the call site because the caller should clearly see that the method can affect the variable. This is intentional language design. A call like Update(ref value) warns the reader that value may be changed.

The in modifier can be written at the call site too, but it is often optional depending on the method and compiler context. Writing it explicitly can improve readability when you want to show that a large value is passed by read-only reference.

ref Locals and ref Returns

C# also supports advanced features called ref locals and ref returns. These allow code to store or return a reference to a variable location. They are useful in performance-sensitive code and low-level APIs, but they are not needed for most beginner or business applications.

For normal method parameter usage, focus first on understanding ref, out, and in. Advanced reference features should be used only when the benefit is clear and the lifetime rules are understood.

Design Checklist for ref, out and in

  • Does the method really need to modify the caller variable?
  • Would a normal return value be clearer?
  • Would a tuple or result object communicate multiple values better?
  • Is in being used for a genuinely large value type?
  • Will the call site remain readable to another developer?

If the answer is unclear, prefer simpler parameter passing. These modifiers should make intent sharper, not more mysterious.

Another important point is that these keywords change how the parameter variable is passed, not the basic meaning of reference types. Passing a class object normally still lets the method change the object’s internal state, but it cannot replace the caller’s variable unless the parameter itself is passed with ref or out. This distinction prevents many beginner bugs.

Common Mistakes with ref, out and in

  • Using ref when returning a value would be clearer.
  • Forgetting that out parameters must be assigned inside the method.
  • Using in for small values where copying is not a real problem.
  • Overusing these keywords and making method calls harder to understand.
  • Confusing pass-by-reference with reference types.

Best Practices for ref, out and in in C#

Use these modifiers only when they make intent clearer or solve a real performance or API problem. Prefer normal return values for simple results, use out for Try-pattern methods, use ref when mutation is intentional, and use in mainly for large read-only structs.