DEV Community

Fabrizio Bagalà
Fabrizio Bagalà

Posted on • Edited on

Ref, Out, In parameters in C#

ℹ️ Information
The code in the following article was tested with C# 8 or above.

In C#, we have various techniques available for controlling the passing of arguments to methods. Among these, the keywords ref, out, and in play a particularly important role. These parameter modifiers allow you to specify the way data is transferred between methods, although they have some limitations.

Ref

The ref keyword is used to pass an argument to a method by reference, rather than by value. This means that if a method changes the value of a ref parameter, this change will also be visible to the variable passed by the caller.

🔎 Insight
In pass by value, a copy of the actual parameter value is created and passed to the function. Any modifications made to the parameter inside the function do not affect the original value outside the function.
In pass by reference, the reference or memory address of the parameter is passed. Any modifications made to the parameter inside the function are reflected in the original value outside the function.

public void SampleMethod(ref int x) 
{
    x = 10;
}

int num = 1;
SampleMethod(ref num);
Console.WriteLine(num); // Prints 10
Enter fullscreen mode Exit fullscreen mode

It's important to highlight that to use a ref parameter, both the method definition and the calling method must explicitly use the ref keyword. Also, the variable passed as a ref parameter must be initialized before being passed to the method.

⚠️ Warning
Do not confuse the concept of passing by reference with the concept of reference types. The two concepts are not the same. A method parameter can be modified by ref regardless of whether it is a value type or a reference type. There is no boxing of a value type when it is passed by reference.

Out

The out keyword is used to pass arguments by reference. Unlike ref, however, out does not require that the input variable be initialized before being passed. Also, the method must necessarily assign a value to the out parameter before it concludes its execution.

public void SampleMethod(out int x) 
{
    x = 10;
}

int num;
SampleMethod(out num);
Console.WriteLine(num); // Prints 10
Enter fullscreen mode Exit fullscreen mode

⚠️ Warning
The out keyword can also be used with a generic type parameter to specify that the type parameter is covariant.

In

The in keyword, introduced in C# 7.2, is used to pass an argument to a method by reference but prevents the value from being modified within the method. This is particularly useful when working with large structures, because it avoids the overhead of copying, while at the same time ensuring the integrity of the data.

public void SampleMethod(in int x) 
{
    // x = 10; // This will generate an error
    Console.WriteLine(x);
}

int num = 1;
SampleMethod(in num); // Prints 1
Enter fullscreen mode Exit fullscreen mode

⚠️ Warning
The in keyword can also be used with a generic type parameter to specify that the type parameter is contravariant, as part of a foreach statement, or as part of a join clause in a LINQ query.

Limitations

Despite their usefulness, the keywords ref, out, and in have some limitations:

  • They cannot be used in async methods defined with the async modifier.
  • They cannot be used in iterator methods that include a yield return or yield break statement.
  • The first argument of an extension method cannot have the in modifier unless that argument is a struct.
  • They cannot be used in the first argument of an extension method in which that argument is a generic type, even when that type is constrained to be a struct.

The restrictions also extend to extension methods:

  • The out keyword cannot be used in the first argument of an extension method.
  • The ref keyword cannot be used in the first argument of an extension method, unless the argument is a struct or an unconstrained generic type.
  • The in keyword cannot be used unless the first argument is a struct. Furthermore, it cannot be used in any generic type, even when it is constrained to be a struct.

Additionally, it's important to remember that members of a class cannot have signatures that differ only by ref, in, or out. In practice, a compiler error will occur if the only difference between two members of a type is that one of them has a ref parameter and the other has an out or in parameter.

⛔️ Compiler Error CS0663
Cannot define overloaded methods that differ only on ref and out.

public class TestClass
{
   public void SampleMethod(out int i) { }
   public void SampleMethod(ref int i) { }
}
Enter fullscreen mode Exit fullscreen mode

Overloading is legal, however, if one method takes a ref, in, or out argument and the other has none of those modifiers, like this:

public class TestClass1
{
    public void SampleMethod(int i) { }
    public void SampleMethod(ref int i) { }
}

public class TestClass2
{
    public void SampleMethod(int i) { }
    public void SampleMethod(out int i) => i = 5;
}

public class TestClass3
{
    public void SampleMethod(int i) { }
    public void SampleMethod(in int i) { }
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

The ref, out, and in keywords provide detailed control over how arguments are passed to methods. Understanding and using them correctly can enhance code efficiency and safety. However, it's crucial to be aware of their limitations and use them cautiously, as improper use can render the code complex and hard to manage.

References

Top comments (2)

Collapse
 
ant_f_dev profile image
Anthony Fung

Great overview.

ref does special things with reference types too. Modifying a reference type passed with ref modifies the original variable (similar to value types passed by reference as per the example above):

void SampleMethod(ref List<int> list)
{
  list = new List<int>();
}

var a = new List<int>();
var b = a;
SampleMethod(ref a);

Assert.IsTrue(a != b);
Enter fullscreen mode Exit fullscreen mode

Without ref, the above test would fail.

However, modifications to the list that's passed (e.g. adding items) would persist both with and without ref.

Collapse
 
fabriziobagala profile image
Fabrizio Bagalà

Thank you for your support 🙏

Nice example! You understood perfectly how the ref parameter works 😊

Changes to the passed list (e.g., adding elements) would persist both with and without ref this because List is an object. If you had a primitive type (e.g., an int), you necessarily needed ref.