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;
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)
return ref data;
int testValue = 1;
ref int pointsToTestValue = ref testValue.Increment();
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
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
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.