ℹ️ 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
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 byref
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
⚠️ Warning
Theout
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
⚠️ Warning
Thein
keyword can also be used with a generic type parameter to specify that the type parameter is contravariant, as part of aforeach
statement, or as part of ajoin
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
oryield 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 onref
andout
.
public class TestClass
{
public void SampleMethod(out int i) { }
public void SampleMethod(ref int i) { }
}
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) { }
}
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.
Top comments (2)
Great overview.
ref
does special things with reference types too. Modifying a reference type passed withref
modifies the original variable (similar to value types passed by reference as per the example above):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
.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 becauseList
is an object. If you had a primitive type (e.g., anint
), you necessarily neededref
.