Inline out variables and ref locals and returns in C# 7

In C#, function parameters can be declared with the ref or out modifier, the former indicating that a value is already set and the function can read and write it, and the later that the value is not set and the function must do so before returning.

As a side note, there is a good thread on this topic on StackOverflow, What’s the difference between the ‘ref’ and ‘out’ keywords?, with one answer nailing the question in a hilarious way.

C# 7 has extended the way these modifiers can be used:

  • out variables can be declared inline and used in the outer scope
  • ref can be used for locals and return values from functions

out variables

Prior to C#7, arguments passed to out parameters had to be declared before they are used. The following is a typical example:

var text = "42";
int value;
int.TryParse(text, out value);
Console.WriteLine(value);

The new change allows the declaration of the out variable inline where it is actually used.

var text = "42";
int.TryParse(text, out var value);
Console.WriteLine(value);

You can either specify an explicit type, or use the var keyword and let the compiler infer the type.
The scope of the variable declared in this manner is leaked into the output scope of the statement where it appears.

ref locals and returns

The ref modifier has been extended to be used with local variables and return values.

Local variables can be declared with the ref modifier, in which case the variable is a reference to another storage. Local references must be initialized upon declaration, and cannot be assigned values (i.e. ref var i = 42;).

var i = 42;
ref var ri = ref i;    // reference to i
ri = 43;
Console.WriteLine(i);  // prints 43

On the other hand, functions can return references to variables. In this case the ref modifier needs to be used both on the return type declaration and on every return statement.

In the following example Container is an implementation of a container that internally stores an array of elements of type T. Method at() takes an argument that represents the index of an element and returns a reference to that element, or throws an exception if the index is out of bounds. The return type is declared as ref T and the return statement is return ref.

class Container<T>
{
   private T[] data;

   public Container(int size = 16)
   {
      data = new T[16];
   }

   public ref T at(int index)
   {
      if(index >= 0 && index < data.Length)
         return ref data[index];

      throw new IndexOutOfRangeException();
   }
}

Having an instance of this Container class we can gain access to its elements in order to read or write them using the at() method. Elements can be assigned new values directly (c.at(0) = 42;) or could be bound to local variables.

var c = new Container<int>();
c.at(0) = 42;
Console.WriteLine(c.at(0)); // prints 42

var r1 = c.at(1);
r1 = 42;
Console.WriteLine(c.at(1)); // prints 0

ref var r2 = ref c.at(2);
r2 = 42;
Console.WriteLine(c.at(2)); // prints 42

There is an important difference between the last two examples:

  • var r1 = c.at(1); the value of the second element in the container is copied to the local variable r1, even though the function returned a reference. Modifying this local variable will not affect the element in the container.
  • ref var r2 = ref c.at(2); defines r2 as a reference to the third element in the container, and therefore modifying the local variable will actually modify the element in the container.

Returning a references to a local variable is not allowed (as its scope ends when the function returns, which would make the reference point to a storage that no longer exists).

public ref int func()
{
   var i = 42;
   return ref i; // error
}

You can read more about these changes in this article, What’s new in C# 7.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.