参照戻り値のサポート (Visual Basic)

C# 言語では ''参照戻り値'' がサポートされています。 参照戻り値は、メソッドに参照渡しされる引数の逆と考えると理解しやすくなります。 参照渡しされた引数が変更されると、変更は呼び出し元の変数の値に反映されます。 メソッドが参照戻り値を呼び出し元に返すと、呼び出し元によって参照戻り値に加えられた変更が、呼び出されたメソッドのデータに反映されます。

Visual Basic では、参照戻り値を使用するメソッドを作成することはできませんが、参照戻り値を使用することは可能です。 つまり、参照戻り値を使用してメソッドを呼び出し、その戻り値を変更できます。参照戻り値に対する変更は、呼び出されたメソッドのデータに反映されます。

参照戻り値を直接変更する

常に成功し、ByRef パラメーターがないメソッドの場合、参照戻り値を直接変更できます。 これを行うには、参照戻り値を返す式に新しい値を割り当てます。

次の C# の例では、内部値をインクリメントし、それを参照戻り値として返す NumericValue.IncrementValue メソッドを定義しています。

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;
   }
}

次の Visual Basic の例では、この参照戻り値が呼び出し元によって変更されます。 NumericValue.IncrementValue メソッド呼び出しを含む行では、メソッドに値を割り当てていないことに注意してください。 代わりに、メソッドによって返される参照戻り値に値を割り当てます。

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

ヘルパー メソッドを使用する

メソッド呼び出しの参照戻り値を直接変更することが必ずしも望ましいとは限らない場合もあります。 たとえば、文字列を返す検索メソッドは、常に一致するものを見つけるとは限りません。 その場合、検索が成功した場合にのみ、参照戻り値を変更します。

次の C# の例はこのシナリオを示しています。 C# で記述された Sentence クラスを定義し、指定された部分文字列で始まる文の次の単語を検索する FindNext メソッドを含めます。 文字列は参照戻り値として返され、参照によりメソッドに渡される Boolean 変数は検索が成功したかどうかを示します。 参照戻り値は、返された値を読み取るだけでなく、呼び出し元がそれを変更することもでき、その変更が Sentence クラスの内部に含まれるデータに反映されることを示しています。

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();
    }
}

この例での参照戻り値の直接変更は信頼できません。メソッド呼び出しで一致するものが見つからず、文の最初の単語が返される可能性があるためです。 その場合、呼び出し元は文の最初の単語を誤って変更することになります。 これは、呼び出し元が null (Visual Basic では Nothing) を返すことによって防ぐことができる場合があります。 ただし、その場合、値が Nothing の文字列を変更しようとすると、NullReferenceException がスローされます。 呼び出し元が String.Empty を返すことによって防ぐことができる場合もありますが、そのためには、値が String.Empty である文字列変数を呼び出し元で定義する必要があります。 呼び出し元はその文字列を変更できますが、変更された文字列は Sentence クラスによって格納された文の単語と関係がないため、変更自体に何の意味もありません。

このシナリオに対応する最良の方法は、ヘルパー メソッドに対して参照戻り値を参照渡しにすることです。 ヘルパー メソッドには、メソッド呼び出しが成功したかどうかを判断し、成功した場合は参照戻り値を変更するロジックを含めます。 次の例は考えられる実装を示しています。

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.

関連項目