C# 7.0 offers a great list of new features. Some of them I’ve already written about, such as expression bodied members, tuples, binary literals, and digit separators. This article shows other new features: out vars, ref locals, and ref returns.
Passing by Value
Declaring a parameter of a value type, the value is allocated within the method and destroyed at the end of the method. Thus, changes to the value does not result in changes within the caller of the method.
Let’s get into an example. The method PassByValue(int x)
receives the copy of a value type. x
is allocated within the method, invoking this method a copy of the variable a
is passed. The change to x
only happens within the scope of the method PassByValue
.
static void PassByValueSample() { int a = 1; PassByValue(a); Console.WriteLine($"after the invocation of {nameof(PassByValue)}, {nameof(a)} = {a}"); } static void PassByValue(int x) { x = 2; }
Because a copy of the value is passed, the value of a
never changes and thus after the invocation of PassByValue, a = 1 is shown at the console.
Passing by Reference
To return changed data from a method, a return type can be used. Another way to return values, is to declare a parameter with the ref modifier. This allows for returning not only one value but multiple values. With ref parameters, the method can receive and return a value.
With the following code snippet, the parameter x of the method PassByReference
has the ref modifier assigned. This modifier means that the invocation happens using a pointer. The ref keyword with C# just offers an easier syntax compared to the pointers from the C programming language. The variable x
references the allocated value of the variable a
. Thus changing the value to 2, the variable a
contains the changed value after the invocation of PassByReference
.
static void PassByReferenceSample() { int a = 1; PassByReference(ref a); Console.WriteLine($"after the invocation of {nameof(PassByReference)}, {nameof(a)} = {a}"); } static void PassByReference(ref int x) { x = 2; }
With the change, the result on the console is after the invocation of PassByReference, a = 2.
Another way to return multiple values is to a) define a custom type, and b) to use a tuple.
Out Vars
With the ref modifier, a value can be passed to and returned from the method. If a value should only be returned from the method, the out modifier can be used.
static void Out(out int x) { x = 2; }
Calling the method out, the variable a
that is passed to the method Out
does not need to be initialized – it is initialized within the method Out
.
static void OutSampleCS6() { int a; Out(out a); Console.WriteLine($"after the invocation of {nameof(Out)}, {nameof(a)} = {a}"); }
Running the application, the value returned from the Out
method is shown: after the invocation of Out, a = 2.
C# 7.0 offers a shorter syntax invoking methods with out parameters. The variable can be declared directly within the invocation. This allows for a one-liner instead the need to have multiple code lines:
static void OutSample() { Out(out int a); Console.WriteLine($"after the invocation of {nameof(Out)}, {nameof(a)} = {a}"); }
If just one value is returned from a method, it’s best to use a return type instead of the out modifier. A useful example for using the out modifier are the TryParse methods that are of the bool, and return the parsed value with the out modifier:
if (int.TryParse("42", out int result)) { Console.WriteLine($"the result is {result}"); }
It’s also possible to use the var keyword declaring the type of the out variable. The type is known from the method declaration:
if (int.TryParse("42", out var result)) { Console.WriteLine($"the result is {result}"); }
C# 7.0 allows calling methods with the out modifier to declare the variable when calling the method. This allows for simpler statements.
Ref Locals
Before C# 7.0 it was not possible to declare the return type with the ref modifier. While this feature was available using IL code, the feature was not reflected with C#. This changes with C# 7.0. However, before looking into this, let’s get into another new C# 7.0 feature: ref locals. Local variables can be declared with the ref modifier. Here, the variable x1
references variable x
, and thus changing x1 changes x as well:
int x = 3; ref int x1 = ref x; x1 = 2; Console.WriteLine($"{nameof(LocalRef)}, local variable {nameof(x)} after the change: {x}");
Ref Returns
The ref keyword can also be used with the return type. This allows code as shown in the following code snippet. Here, an array of type int is declared and initialized. In the next line, a local ref variable is declared that references the first element of the array. This variable is then returned in the last statement of the method:
static ref int ReturnByReference() { int[] arr = { 1 }; ref int x = ref arr[0]; return ref x; }
Instead of declaring a local ref variable, the array element can be returned directly:
static ref int ReturnByReference() { int[] arr = { 1 }; return ref arr[0]; }
Returning a local int variable instead of an array is not possible. int is a value type, and thus the variable gets out of scope at the end of the method, and thus a reference to it cannot be returned. That’s different with an array. An array is a reference type, and the array is allocated on the heap. An int within the array can be returned with the ref keyword.
While it is not possible to return a local int variable with ref, this is possible with a parameter that is passed by reference:
static ref int ReturnByReference2(ref int x) { x = 2; return ref x; }
When are ref returns really useful. You will not see this feature in all programs, most programs don’t get any advantage of this new feature. However, for some programs this is really a great extension. In many cases, unsafe code is no longer needed for fast performance. Let’s assume returning a slice of an array.
Let’s have a look at the mehtod GetByIndex
. This method receives an int array with an index into this array, and returns a reference to the indexed element:
static ref int GetByIndex(int[] arr, int ix) => ref arr[ix];
Now it’s possible to change the elements of the array:
static void FastAccessToArrayElements() { int[] arr = { 1, 2, 3, 4, 5 }; ref int x = ref GetByIndex(arr, 2); x = 42; Console.WriteLine($"arr[2] after it was changed using a ref return: {arr[2]}"); }
Summary
The ref modifier already existed with C# since the first version. However, it could only be applied to parameters and not returns and local variables. This changes with C# 7.0, and adds features that already have been available with IL code. With the out modifier where the variable does not need to be initialized with the caller, now the variable can be declared on method invocation. This not only reduces the number of code lines, but also often allows for using expression bodied members when only a single statement is required.
Related
Other C# 7.0 features I’ve already written about:
Binary Literals and Digit Separators
Sample Code
The sample code is available at GitHub.
To run the sample code you need Visual Studio 2017 RC.
Have fun with programming and learning!
Christian
More Information
Update: more information on C# 7 features in my new book Professional C# 7 and .NET Core 2.0 with source code updates at GitHub.
More information about C# and expression-bodied members is available in my new book and my C# workshops:
Professional C# 6 and .NET Core 1.0
Image from © Sebastian Czapnik | Dreamstime.com Reference Book
6 thoughts on “C# 7.0 Out Vars and Ref Returns”