ref (C# リファレンス)

ref キーワードは、次のコンテキストで使用できます。

  • メソッド シグネチャとメソッドの呼び出しで、参照によってメソッドに引数を渡します。 詳細については、「参照渡しで引数を渡す」を参照してください。
  • メソッド シグネチャで、参照渡しで呼び出し元に値を返します。 詳細については、参照戻り値に関するページを参照してください。
  • ローカル変数の宣言で、参照変数を宣言する目的で。 詳細については、「宣言ステートメント」の記事の「参照変数」セクションを参照してください。
  • 条件付き ref 式または ref 代入演算子の一部として。
  • struct 宣言で、ref struct を宣言する目的で。 詳細については、ref構造体の種類の記事を参照してください。
  • ref struct 定義で、ref フィールドを宣言します。 詳細については、「ref構造体型」の記事の「refフィールド」セクションを参照してください。

参照渡しで引数を渡す

メソッドのパラメーター リストで使用した場合、ref キーワードは、引数を値ではなく、参照によって渡すことを示します。 ref キーワードは、仮パラメーターを引数 (変数にする必要があります) の別名にします。 つまり、パラメーターに対するすべての操作は引数に対して行われます。

たとえば、呼び出し元からローカル変数式または配列要素のアクセス式が渡されるとします。 また、呼び出されるメソッドでは、ref パラメーターが参照するオブジェクトを置き換えることができます。 その場合は、メソッドから制御が戻ったとき、呼び出し元のローカル変数または配列要素によって参照されるのは、新しいオブジェクトとなります。

注意

参照渡しで渡すという概念と参照型の概念とを混同しないでください。 2 つの概念は同じではありません。 メソッドのパラメーターは、値型か参照型かどうかに関係なく、ref によって変更できます。 参照渡しで渡される場合、値型はボックス化されません。

ref パラメーターを使用するには、メソッド定義と呼び出し元のメソッドの両方が、次の例に示すように ref キーワードを明示的に使用する必要があります。 (例外として、COM 呼び出しを行う場合は、呼び出し元のメソッドで ref を省略できます)。

void Method(ref int refArgument)
{
    refArgument = refArgument + 44;
}

int number = 1;
Method(ref number);
Console.WriteLine(number);
// Output: 45

ref または in パラメーターに渡す引数は、渡す前に初期化する必要があります。 この要件は、引数を渡す前に明示的に初期化する必要がない out パラメーターの場合とは異なります。

クラスのメンバーは、refin、または out のみが異なるシグネチャを持つことはできません。 1 つの型の 2 つのメンバー間の唯一の違いが、1 つには ref パラメーターが存在し、もう 1 つには out または in パラメーターが存在することである場合、コンパイラ エラーが発生します。 たとえば、次のコードはコンパイルされません。

class CS0663_Example
{
    // Compiler error CS0663: "Cannot define overloaded
    // methods that differ only on ref and out".
    public void SampleMethod(out int i) { }
    public void SampleMethod(ref int i) { }
}

ただし、次の例に示すように、1 つのメソッドに refin または out パラメーターがあり、もう 1 つには値渡しされるパラメーターがある場合、メソッドをオーバーロードすることができます。

class RefOverloadExample
{
    public void SampleMethod(int i) { }
    public void SampleMethod(ref int i) { }
}

非表示やオーバーライドなど、シグネチャの一致が必要な他の状況では、inrefout はシグネチャの一部であり、互いに一致しません。

プロパティは変数ではありません。 それらはメソッドであり、ref パラメーターに渡すことはできません。

次の種類のメソッドには、refinout キーワードを使用することはできません。

  • async 修飾子を使用して定義した Async メソッド。
  • yield return または yield break ステートメントを含む Iterator メソッド。

拡張メソッドには、これらのキーワードの使用に関する制限もあります。

  • 拡張メソッドの最初の引数では、out キーワードを使用できません。
  • 拡張メソッドの最初の引数が構造体ではない場合、または構造体として制約されていないジェネリック型である場合、その引数で ref キーワードを使用することはできません。
  • 最初の引数が構造体である場合を除き、in キーワードは使用できません。 ジェネリック型では、構造体として制約されている場合であっても、in キーワードを使用することはできません。

参照渡しで引数を渡す:使用例

前の例は、参照によって値型を渡す例でした。 ref キーワードを使用して、参照渡しで参照型を渡すこともできます。 参照型を参照渡しで渡すと、呼び出されたメソッドは、参照パラメーターが呼び出し元で参照するオブジェクトに置換できます。 オブジェクトの格納場所は、参照パラメーターの値としてメソッドに渡されます。 パラメーターの格納場所の値を変更する場合は (新しいオブジェクトをポイント)、呼び出し元が参照する格納場所を変更することもできます。 次の例では、参照型のインスタンスを ref パラメーターとして渡します。

class Product
{
    public Product(string name, int newID)
    {
        ItemName = name;
        ItemID = newID;
    }

    public string ItemName { get; set; }
    public int ItemID { get; set; }
}

private static void ChangeByReference(ref Product itemRef)
{
    // Change the address that is stored in the itemRef parameter.
    itemRef = new Product("Stapler", 99999);

    // You can change the value of one of the properties of
    // itemRef. The change happens to item in Main as well.
    itemRef.ItemID = 12345;
}

private static void ModifyProductsByReference()
{
    // Declare an instance of Product and display its initial values.
    Product item = new Product("Fasteners", 54321);
    System.Console.WriteLine("Original values in Main.  Name: {0}, ID: {1}\n",
        item.ItemName, item.ItemID);

    // Pass the product instance to ChangeByReference.
    ChangeByReference(ref item);
    System.Console.WriteLine("Back in Main.  Name: {0}, ID: {1}\n",
        item.ItemName, item.ItemID);
}

// This method displays the following output:
// Original values in Main.  Name: Fasteners, ID: 54321
// Back in Main.  Name: Stapler, ID: 12345

参照型を値渡しまたは参照渡しで渡す方法の詳細については、「参照型パラメーターの引き渡し」を参照してください。

参照戻り値

参照戻り値 (または ref 戻り値) は、メソッドから呼び出し元に参照渡しで返される値です。 つまり、呼び出し元はメソッドによって返される値を変更することができ、その変更は呼び出されたメソッド内のオブジェクトの状態に反映されます。

参照戻り値は ref キーワードを使用して以下に定義されます。

  • メソッド シグネチャ。 たとえば、次のメソッド シグネチャは、GetCurrentPrice メソッドが参照渡しで Decimal 値を返すことを示しています。
public ref decimal GetCurrentPrice()
  • メソッドの return ステートメントで返される変数と return トークンの間。 次に例を示します。
return ref DecimalArray[0];

呼び出し元がオブジェクトの状態を変更するには、参照戻り値を、参照変数として明示的に定義された変数に格納する必要があります。

次に、メソッド シグネチャとメソッド本体の両方を示す、より完全な ref 戻り値の例を示します。

public static ref int Find(int[,] matrix, Func<int, bool> predicate)
{
    for (int i = 0; i < matrix.GetLength(0); i++)
        for (int j = 0; j < matrix.GetLength(1); j++)
            if (predicate(matrix[i, j]))
                return ref matrix[i, j];
    throw new InvalidOperationException("Not found");
}

呼び出されるメソッドでは、値が参照渡しで値が返されるように戻り値を ref readonly として宣言し、返された値を呼び出し元のコードで変更できないようにすることもできます。 呼び出し元のメソッドを使うと、ローカルの ref readonly 参照変数に値を格納することで、返された値のコピーを回避できます。

例については、「ref 戻り値と ref ローカル変数の使用例」を参照してください。

ref 戻り値と ref ローカル変数の使用例

次の例は、TitleAuthor という 2 つの String フィールドを持つ Book クラスを定義しています。 また、Book オブジェクトのプライベート配列を含む BookCollection クラスも定義しています。 個々のブック オブジェクトは、GetBookByTitle メソッドを呼び出すことによって参照渡しで返されます。


public class Book
{
    public string Author;
    public string Title;
}

public class BookCollection
{
    private Book[] books = { new Book { Title = "Call of the Wild, The", Author = "Jack London" },
                        new Book { Title = "Tale of Two Cities, A", Author = "Charles Dickens" }
                       };
    private Book nobook = null;

    public ref Book GetBookByTitle(string title)
    {
        for (int ctr = 0; ctr < books.Length; ctr++)
        {
            if (title == books[ctr].Title)
                return ref books[ctr];
        }
        return ref nobook;
    }

    public void ListBooks()
    {
        foreach (var book in books)
        {
            Console.WriteLine($"{book.Title}, by {book.Author}");
        }
        Console.WriteLine();
    }
}

呼び出し元が GetBookByTitle によって返される値を ref ローカル変数として格納する場合、呼び出し元が戻り値に加えた変更が BookCollection オブジェクトに反映されます。次の例を参照してください。

var bc = new BookCollection();
bc.ListBooks();

ref var book = ref bc.GetBookByTitle("Call of the Wild, The");
if (book != null)
    book = new Book { Title = "Republic, The", Author = "Plato" };
bc.ListBooks();
// The example displays the following output:
//       Call of the Wild, The, by Jack London
//       Tale of Two Cities, A, by Charles Dickens
//
//       Republic, The, by Plato
//       Tale of Two Cities, A, by Charles Dickens

C# 言語仕様

詳細については、「C# 言語の仕様」を参照してください。 言語仕様は、C# の構文と使用法に関する信頼性のある情報源です。

関連項目