Pass by Reference: out, ref, in

out, ref and in Keywords in C# .NET

Jiyan Epözdemir
C# Programming
Published in
7 min readApr 19, 2024

--

Today, we’re going to explore how C# .NET pass parameters to methods. By default, as you know, parameters are passed to a method by value. When pass by value, the function receives a copy of the real parameter value. The parameter’s initial value outside of the function remains unchanged despite any changes made within the method. In pass by reference, the parameter’s reference or memory address is passed. Inside the function, all changes made to the parameter are reflected in the original value outside the function.

The C# language provides three keywords — out, ref and in — that play a significant role in parameter passing. These modifier keywords allow us to pass a parameter by reference. They indicate that an argument/parameter is passed by reference to methods rather than by value.

In this blog post, we’ll cover the use of these parameter modifiers, their differences, limitations, and when to use them with practical C# examples.

By understanding how they work, you’ll be able to use their power to write more efficient and flexible code.

What is out, ref and in Keywords

The out, refand in keywords are widely used keywords in C# which allow us to make better abstractions for data types and methods.

Before diving into the specifics, let’s clarify the roles of these keywords:

out: Specifies that a parameter is passed by reference and must be assigned a value within the method.

ref: Similar to out, ref also passes parameters by reference, but unlike out, it does not require the parameter to be assigned within the method.

in: Indicates that a parameter is passed by reference but ensures that it cannot be modified within the method.

The “out“ Keyword

The outkeyword is used when you want to pass a parameter by reference. It ensures that the parameter must be assigned a value within the method. Unlike ref, out does not require that the input variable be initialized before being passed.

Here’s an example:

void OutSample(out int a, out int b)
{
a = 10;
b = 20;
}

static void Main(string[] args)
{
int x, y;
OutSample(out x, out y);
Console.WriteLine($"Value of x: {x}, Value of y: {y}");
}

OutSample takes two out parameters a and b. When a method parameter is declared with the out keyword, it indicates that the parameter is passed by reference and must be initialized by the method before it returns.

Note that, OutSamplemethod declared as void method that doesn’t return any value. Inside the method, a is assigned the value 10, and b is assigned the value 20, but doesn’t produce any result that needs to be returned to the caller.

In the Main method, two integer variables x and y are declared but not initialized.

Then, the OutSample method is called with x and y passed as out parameters. This means that OutSample will initialize these variables within the method. After the method call, x will hold the value 10 and y will hold the value 20.

Finally, the Console.WriteLine statement prints the values of x and y.

So, the output of this program will be:

Value of x: 10, Value of y: 20

Unlike ref parameters, out parameters must be assigned a value within the method before they are used. This can lead to code verbosity as you need to initialize the parameter before using it, even if you intend to overwrite its value.

out parameters are designed for returning values from a method, but they cannot be used to pass data into a method. This limitation restricts their usage to specific scenarios where a method needs to return multiple values.

The “ref“ Keyword

Similar to out, the refkeyword is also used when you want to pass a parameter by reference and allow the method to modify its value. This implies that if a method modifies a refparameter’s value, the caller’s passed variable will also change.

void RefSample(ref int value)
{
value *= 2;
}

static void Main(string[] args)
{
int num = 5;
RefSample(ref num);
Console.WriteLine($"Modified value: {num}");
}

In this C# example, a method called RefSample is defined with a parameter declared using the ref keyword.

RefSample takes a single parameter value by reference (ref int value). Inside the method, the value of the value parameter is doubled (*2). Since value is passed by reference, this change will be reflected in the original variable passed in by the caller.

In the Main method, an integer variable num is declared and initialized with the value 5. The RefSample method is called with num passed in as an argument using the ref keyword.

Since num is passed by reference, any changes made to it inside RefSample will affect the original variable.

After the method call, the Console.WriteLine statement prints the value of num. Since RefSample doubled the value of num, the output will be :

Modified value: 10

It is important to distinguish between the concepts of reference types and passing by reference. The two concepts are not interchangeable. Whether a method parameter is a value type or a reference type, it can be changed by ref keyword.

Parameters marked with ref allow bidirectional communication between the method and the caller, enabling modification of the parameter's value within the method. While this flexibility can be powerful, it also introduces the risk of unintended side effects and makes code harder to reason about.

The use of ref parameters can bypass certain safety features of C# such as nullability checks. This can potentially lead to runtime errors if not used carefully.

The “in“ Keyword

The inkeyword, introduced in C# 7.2, is also used when you want to pass a parameter by reference, but ensure that it remains immutable within the method. This is especially useful when working with huge structures because it eliminates the overhead of copying while also ensuring the integrity of the data.

void InSample(in int value)
{
// value++; // This will result in a compile-time error since 'value' is read-only
Console.WriteLine($"Value received: {value}");
}

static void Main(string[] args)
{
int number = 10;
InSample(number);
Console.WriteLine($"Original value: {number}");
}

In this C# example, a method called InSample is defined with a parameter declared using the in keyword.

InSample is a method that takes a single parameter value using the in keyword. The in keyword indicates that the parameter is passed by reference, but it is treated as read-only within the method. This means that the method can access the value of the parameter but cannot modify it.

Inside the method, an attempt is made to increment the value parameter (value++). However, if you attempt to uncomment the line // value++; and try to modify the value parameter inside the InSample method, you will encounter the following compile-time error:

Error CS8331: Cannot assign to variable 'in int' because it is a readonly variable

This operation results in a compile-time error because value is declared with the in keyword, making it read-only within the method.

In the Main method, an integer variable number is declared and initialized with the value 10.

The InSample method is called with number passed in as an argument. Since number is passed by reference as an in parameter, its value is accessed by the InSample method, but the method cannot modify it.

At last, the program simply prints the value of the value parameter using Console.WriteLine. Since InSample does not modify number, the output will be :

Value received: 10
Original value: 10

Parameters marked with in are treated as read-only within the method. This means you cannot modify their values inside the method. While this ensures safety and prevents unintended modifications, it can be restrictive if you need to modify the parameter's value.

While in parameters can provide performance benefits by avoiding unnecessary copying of large value types, they might introduce overhead for smaller value types due to the additional indirection required.

Limitations of out, ref and in

While out, refand in parameters offer powerful capabilities for passing parameters to methods in C# .NET, they also come with some limitations. Here are the limitations of each:

  • out, refand in keywords cannot be used in async methods defined with the async modifier.
  • Iterator methods with a yield return or yield break statement cannot use them.
  • An extension method’s first parameter cannot have the inmodifier unless it 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.
  • When determining overloads, the parameter modifiers out, refand in are not included in the method signature. Therefore, methods that differ simply in signature with respect to these keywords cannot be overloaded. Your code won’t compile at all if you have two methods with the same name, but one of them takes an integer as an inparameter and the other takes an integer as an outparameter.

Conclusion

Understanding how to use the out, refand in keywords can improve code efficiency and safety in .NET applications. By utilizing these keywords appropriately, you can ensure proper parameter passing and manipulation within your methods.

Remember, infor immutability, outfor return values, and reffor bidirectional parameter passing.

Using these parameter modifiers properly will provide more comprehensive control over how arguments are passed to methods.

Thank you for reading!

If you found this post helpful, don’t forget to give it a clap and share it with others who might benefit from it.👏👏👏👏👏

--

--

Jiyan Epözdemir
C# Programming

I am a multi-disciplined Software Architect and Computer Engineer, MSc. I enjoy building awesome softwares & applications.