ℹ️ 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 byrefregardless 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
Theoutkeyword 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
Theinkeyword can also be used with a generic type parameter to specify that the type parameter is contravariant, as part of aforeachstatement, or as part of ajoinclause 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
asyncmodifier. - They cannot be used in iterator methods that include a
yield returnoryield breakstatement. - The first argument of an extension method cannot have the
inmodifier 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
outkeyword cannot be used in the first argument of an extension method. - The
refkeyword cannot be used in the first argument of an extension method, unless the argument is a struct or an unconstrained generic type. - The
inkeyword 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 onrefandout.
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.
refdoes special things with reference types too. Modifying a reference type passed withrefmodifies 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
refparameter works 😊Changes to the passed list (e.g., adding elements) would persist both with and without
refthis becauseListis an object. If you had a primitive type (e.g., anint), you necessarily neededref.