C#7 - Out, Ref Local and Ref Return Are Bringing Some Cool Tricks
22 June 2017

With new return ref I feel like I'm going back to my youth and good old C++. But for the start let's talk about smaller change first (out shortcut).

Out

out improvement is small but useful.  Now you can push out variable declaration inside function call so you don't have extra line above just for variable declaration (value1 and value3 below are declared inside function call). You can also use var since parameter type now can be inferred:

public bool ReadSensors(out int s1, out int s2, out int s3)
{
s1 = 111;
s2 = 222;
s3 = 333;

return true;
}

[Fact]
public void ShortOutSyntaxTest()
{
// Declaration of "out" variables moved to the function call and therefore we can use "var"
// _ is wildcard: we don't care for second parameter
if(ReadSensors(out var value1, out _, out int value3))
{
Assert.Equal(111, value1);
Assert.Equal(333, value3);
}
else
{
Assert.True(false);
}

// Values still in scope:
Assert.Equal(444, value1 + value3);
}

Two things to note from example above. You can use wildcard _ to skip some variables and variables scope is not limited to the if block only.

Return ref

Imagine you are writing monitoring system for a factory. Machine has sensors:

public class Machine
{
public int Id { get; set; }
public double[] Sensors = { 1.1, 2.2, 3.3 };
}

You would like to have code outside machine to manipulate those sensors. Classic solution is to abstract machine into a simple interface and pass it to a sensor manipulation code. With new return ref you can make code even simpler. You can return sensor itself and allow it to be manipulated from outside code (SecondSensor() is returning ref double):

public class Machine
{
public int Id { get; set; }
public double[] Sensors = { 1.1, 2.2, 3.3 };

public ref double SecondSensor()
{
return ref Sensors[1];
}
}

[Fact]
public void RefReturnTest()
{
var machine = new Machine();

Assert.Equal(2.2, machine.Sensors[1]);

// Get a reference to a second sensor & directly change sensor value
ref var secondSensor = ref machine.SecondSensor();
secondSensor = 2.0;
Assert.Equal(2.0, machine.Sensors[1]);

// You can still get by value but change will be local
var secondSensorVal = machine.SecondSensor();
Assert.Equal(2.0, secondSensorVal);
secondSensorVal = 1.9;
Assert.Equal(2.0, machine.Sensors[1]); // Still 2.0
}

As per example above you can use ref and "point" to a sensor (like good old C++ or unsafe C#). Any change on a referenced variable will change machine sensor itself. The only downside is that you have to repeat ref keyword a lot! What I like is that you can omit ref and get sensor value only.

Now the fun part. You can have function on the left side of equation with simple value type (if function is returning reference to a variable):

[Fact]
public void MethodOnTheLeftTest()
{
var machine = new Machine();

machine.SecondSensor() = 2.3;

Assert.Equal(2.3, machine.Sensors[1]);
}

Code above is just an example what is possible and for sure not recommended style.

You can refer to a local variables:

[Fact]
public void RefLocalTest()
{
var local = 555;

ref var referenceToLocal = ref local;

referenceToLocal = 444;

Assert.Equal(444, local);
}

Good news is that compiler will prevent you from doing "impossible" references.

You can't return local variable since it will disappear from the stack as soon as method returns:

// Can't return local variable
public ref int CantReturnLocal()
{
int local = 555;
return ref local; // ERROR
}

Since array goes on a heap you can return local array:

// Can return local array since it is on a heap
public ref int StillReturnLocal()
{
int[] localArray = { 555 };
return ref localArray[0];
}

Don't worry, compiler will prevent you from returning immutable:

// Can't return immutable
public readonly string X = "immutable";
public ref string CantReturnImmutable()
{
return ref X; // ERROR
}

Conclusion

C# is lifting some of its limitations but still it is a safe language. Returning reference is not that important to all users, but if you are pushing performance to the maximum you will appreciate it for sure. Also, out declaration shortcut is a very useful change.