Support for reference return values (Visual Basic)

Starting with C# 7.0, the C# language supports reference return values. One way to understand reference return values is that they are the opposite of arguments that are passed by reference to a method. When an argument passed by reference is modified, the changes are reflected in value of the variable on the caller. When an method provides a reference return value to a caller, modifications made to the reference return value by the caller are reflected in the called method's data.

Visual Basic does not allow you to author methods with reference return values, but it does allow you to consume reference return values. In other words, you can call a method with a reference return value and modify that return value, and changes to the reference return value are reflected in the called method's data.

Modifying the ref return value directly

For methods that always succeed and have no ByRef parameters, you can modify the reference return value directly. You do this by assigning the new value to the expressions that returns the reference return value.

The following C# example defines a NumericValue.IncrementValue method that increments an internal value and returns it as a reference return value.

using System;

public class NumericValue
{
   private int value = 0;
       
   public NumericValue(int value)
   {
      this.value = value;
   }     
   
   public ref int IncrementValue()
   {
      value++;
      return ref value;
   }
   
   public int GetValue()
   {
      return value;
   }
}

The reference return value is then modified by the caller in the following Visual Basic example. Note that the line with the NumericValue.IncrementValue method call does not assign a value to the method. Instead, it assigns a value to the reference return value returned by the method.

Module Example
   Public Sub Main()
      Dim n As New NumericValue(15)
      n.IncrementValue() += 12
      Console.WriteLine(n.GetValue) 
   End Sub
End Module
' Output:   28

Using a helper method

In other cases, modifying the reference return value of a method call directly may not always be desirable. For example, a search method that returns a string may not always find a match. In that case, you want to modify the reference return value only if the search is successful.

The following C# example illustrates this scenario. It defines a Sentence class written in C# includes a FindNext method that finds the next word in a sentence that begins with a specified substring. The string is returned as a reference return value, and a Boolean variable passed by reference to the method indicates whether the search was successful. The reference return value indicates that the caller can not only read the returned value; he or she can also modify it, and that modification is reflected in the data contained internally in the Sentence class.

using System;
 
public class Sentence
{
    private string[] words;
    private int currentSearchPointer;
    
    public Sentence(string sentence)
    {
        words = sentence.Split(' ');
        currentSearchPointer = -1;
    }
    
    public ref string FindNext(string startWithString, ref bool found)
    {
        for (int count = currentSearchPointer + 1; count < words.Length; count++)
        {
            if (words[count].StartsWith(startWithString))
            {
                currentSearchPointer = count;
                found = true;
                return ref words[currentSearchPointer];
            }
        }
        currentSearchPointer = -1;
        found = false;
        return ref words[0];
    }
    
    public string GetSentence()
    {
        string stringToReturn = null;
        foreach (var word in words)
            stringToReturn += $"{word} ";
      
        return stringToReturn.Trim();    
    }
}

Directly modifying the reference return value in this case is not reliable, since the method call may fail to find a match and return the first word in the sentence. In that case, the caller will inadvertently modify the first word of the sentence. This could be prevented by the caller returning a null (or Nothing in Visual Basic). But in that case, attempting to modify a string whose value is Nothing throws a NullReferenceException. If could also be prevented by the caller returning String.Empty, but this requires that the caller define a string variable whose value is String.Empty. While the caller can modify that string, the modification itself serves no purpose, since the modified string has no relationship to the words in the sentence stored by the Sentence class.

The best way to handle this scenario is to pass the reference return value by reference to a helper method. The helper method then contains the logic to determine whether the method call succeeded and, if it did, to modify the reference return value. The following example provides a possible implementation.

Module Example
   Public Sub Main()
      Dim sentence As New Sentence("A time to see the world is now.")
      Dim found = False
      Dim returns = RefHelper(sentence.FindNext("A", found), "A good", found) 
      Console.WriteLine(sentence.GetSentence()) 
   End Sub
   
   Private Function RefHelper(ByRef stringFound As String, replacement As String, success As Boolean) _ 
                    As (originalString As String, found As Boolean) 
      Dim originalString = stringFound
      If found Then stringFound = replacement
      Return (originalString, found)   
   End Function
End Module
' The example displays the following output:
'      A good time to see the world is now.

See also