Ref Value Type

In an earlier post, we discussed the readability factor of the reference semantics, mentioning how it kind of makes the code less readable. However, that doesn’t take away the big door of opportunities the new features add. Consider the following scenario.

As a developer, you want to develop a extension method called Increment
for Type "int", which increments the value by 1.
The extension method should have a void return type.
We would ideally would like to do the following.

int testValue = 1;
testValue.Increment();
Console.WriteLine(testValue);   // This should print a value 2;

Now, prior to C# 7, this was difficult. There was no way we could pass a reference of a value type to an extension method. Which means that every time we call the extension method, we would be passing a copy of the value, rather than the reference, increment the copied value and then just throw away the value. The above code would print a value “1”.

This is where the new reference type comes in play. We could now use reference type in extension methods. Let’s write the code using C# 7 now.
public static int Increment(this ref int Val) => ++Val;
Now if we were to run our code, testValue would show a value “2” as desired. That’s interesting right ?.  Of course, there are other befits too. Consider the scenario where you have a gigantic struct (it is another question whether it is ideal to use a value type when the type is expected to be huge, but for sake of the demonstration we will use struct here) which gets passed to different methods. Each type you are calling the method, you are creating a copy of the gigantic struct (allocating memory).  This is going to be troublesome if you are working on a memory critical environment and the struct is going to be passed around.
With C# 7.0, you can not just pass the value type as reference, but we can also return a value type as ref. That is just brilliant by the language developers right. Let’s go ahead and look into the syntax of the same by modifying our extension method so that it returns a reference as well.
public static ref int Increment(this ref int data)
{
++data;
return ref data;
}

int testValue = 1;
ref int pointsToTestValue = ref testValue.Increment();
Console.WriteLine(testValue);
pointsToTestValue++;
Console.WriteLine(testValue);

The Increment method increments the value as it did earlier. Furthermore it now returns the same value as reference. Do note the changes in syntax for invoking as well as defining a method that return ref.

The output of above code would be

2
3

There might be scenarios where you might have to pass a data as reference, but ensure it doesn’t change. Here is where the new ‘in’ parameter comes in. The “in” keywords accepts the data by reference, but ensure that a developer who works on in future doesn’t change the value accidentally. “in” can be used in interface as well, to ensure that the implementing classes doesn’t change the value in the method.

public int Increment(in int data)
{
++data; // this is not allowed
return data;
}
Update : 
You could also make a returned reference read only, though we would go back to using the readonly keyword rather than “in”. (makes sense too). The syntax would be as follows.

 

static public ref readonly int Increment(ref int data) => ref data;

ref readonly int readonlyReference = ref Increment(ref h);
readonlyReference++; // This is not allowed.

One obvious question of having a reference return type would be , what happens to the scope ? Does that mean GC needs to be aware of references outside scope of a method ? Not exactly, the Language team has thought well about it and has implemented certain safety constrains. The method can only return a ref that either came as a parameter or are in same heap. You cannot return a reference that is created within the method.

We will cover some more features of C# 7.x in coming posts, especially the ones that has to do with reference semantics.

Reference Semantics and Code Readability

One of the features Microsoft packed with C# 7.2 is Reference Semantics with Value Types. Though designed with the intention to improve performance by eliminating the need for copying (and thereby allocating memory) value types , I do have my reservations on the complexity it adds to readability of code for a programmer. Let’s consider the case of “in” keyword.

 

The “in” allows the programmer to pass a parameter by reference, but on the condition that the method cannot modifying the value.  For example
void IncrementAndPrint(in int Counter)
{
    Counter++;   // this will throw error.
    Console.WriteLine(Counter);
}

void Print(in int Counter)
{
    Console.WriteLine(Counter);
}

We could delve into details of “in” on a later post, however, for this post, we will focus on the readability factor. One of the features of “in”, which separates it out from “ref” and “out” is that, you don’t necessarily (yeah…keyword is optional during invocation) use the keyword before using it. For example, for the above method Print , both following method invocation is valid.

int counterValue = 3;
Print(in counterValue);
Print(counterValue);

However, the framework restricts you from passing an expression or direct value with the “in” parameter. This restriction is not valid if you are not using the optional “in” keyword.

Print(in 3); // Invalid code
Print(3);      // This is valid.

Another scenario worth consideration is when there is an overloaded method of Print which doesn’t use the pass by reference. Consider the following two overloaded methods.

void Print(in int Counter)
{
     Console.WriteLine($"Pass By Reference :{Counter}");
}
void Print(int Counter)
{
     Console.WriteLine($"Pass By Value :{Counter}");
}

Understandably, the first method uses “in” keyword to pass the parameter by reference, while the second passes the parameter by value. Considering the “in” parameter is optional, what would be the output of following method invocations.

int num = 3;
Print(in num);
Print(num);

Output

Pass By Reference : 3
Pass By Value : 3

It is on expected lines as Microsoft would have wanted, however, the question is, does the ability of “optional in” do justice to readability of code ?