宣言ステートメント

宣言ステートメントでは、新しいローカル変数、ローカル定数、またはローカル参照変数を宣言します。 ローカル変数を宣言するには、その型とその名前を指定します。 次の例に示すように、1 つのステートメントで同じ型の複数の変数を宣言できます。

string greeting;
int a, b, c;
List<double> xs;

宣言ステートメントでは、変数をその初期値で初期化することもできます。

string greeting = "Hello";
int a = 3, b = 2, c = a + b;
List<double> xs = new();

前の例では、変数の型を明示的に指定しています。 また、コンパイラに初期化式から変数の型を推論させることができます。 これを行うには、型の名前の代わりに var キーワードを使用します。 詳細については、「暗黙的に型指定されたローカル変数」セクションを参照してください。

ローカル定数を宣言するには、次の例に示すように、const キーワードを使用します。

const string Greeting = "Hello";
const double MinLimit = -10.0, MaxLimit = -MinLimit;

ローカル定数を宣言するときは、それを初期化する必要もあります。

ローカル参照変数の詳細については、「参照変数」セクションを参照してください。

暗黙的に型指定されたローカル変数

ローカル変数を宣言するときに、コンパイラに初期化式から変数の型を推論させることができます。 これを行うには、型の名前の代わりに var キーワードを使用します。

var greeting = "Hello";
Console.WriteLine(greeting.GetType());  // output: System.String

var a = 32;
Console.WriteLine(a.GetType());  // output: System.Int32

var xs = new List<double>();
Console.WriteLine(xs.GetType());  // output: System.Collections.Generic.List`1[System.Double]

前の例に示すように、暗黙的に型指定されたローカル変数は厳密に型指定されます。

注意

有効な Null 許容認識コンテキスト var を使用し、初期化式の型が参照型である場合、コンパイラでは、初期化式の型が Null 許容でない場合でも、常に Null 許容 参照型を推論します。

var は、コンストラクターの呼び出し式と共に使用するのが一般的です。 var を使用すると、次の例に示すように、変数宣言およびオブジェクトのインスタンス化において型名を繰り返す必要がなくなります。

var xs = new List<int>();

代替として、ターゲット型の newを使用できます。

List<int> xs = new();
List<int>? ys = new();

匿名型を使う場合は、暗黙的に型指定されたローカル変数を使用する必要があります。 次の例は、匿名型を使用して顧客の名前と電話番号を保持するクエリ式を示しています。

var fromPhoenix = from cust in customers
                  where cust.City == "Phoenix"
                  select new { cust.Name, cust.Phone };

foreach (var customer in fromPhoenix)
{
    Console.WriteLine($"Name={customer.Name}, Phone={customer.Phone}");
}

前の例では、fromPhoenix 変数の型を明示的に指定することはできません。 型は IEnumerable<T> ですが、この場合、T は匿名型であり、その名前を指定することはできません。 そのため、var を使用する必要があります。 同じ理由から、foreach ステートメントで customer 反復変数を宣言するときは、var を使用する必要があります。

暗黙的に型指定されたローカル変数の詳細については、「暗黙的に型指定されたローカル変数」を参照してください。

パターン マッチングでは、var キーワードが var パターンで使用されます。

参照変数

ローカル変数を宣言し、変数の型の前に ref キーワードを追加する場合は、"参照変数" または ref ローカルを宣言します。

ref int alias = ref variable;

参照変数は、別の変数を参照する変数であり、referent (リファレント) と呼ばれます。 つまり、参照変数は、そのリファレントの "別名" です。 参照変数に値を代入した場合、その値はリファレントに代入されます。 参照変数の値を読み取ると、リファレントの値が返されます。 次の例は、その動作を示します。

int a = 1;
ref int alias = ref a;
Console.WriteLine($"(a, alias) is ({a}, {alias})");  // output: (a, alias) is (1, 1)

a = 2;
Console.WriteLine($"(a, alias) is ({a}, {alias})");  // output: (a, alias) is (2, 2)

alias = 3;
Console.WriteLine($"(a, alias) is ({a}, {alias})");  // output: (a, alias) is (3, 3)

次の例に示すように、ref 代入演算子= refを使用して、参照変数のリファレントを変更します。

void Display(int[] s) => Console.WriteLine(string.Join(" ", s));

int[] xs = [0, 0, 0];
Display(xs);

ref int element = ref xs[0];
element = 1;
Display(xs);

element = ref xs[^1];
element = 3;
Display(xs);
// Output:
// 0 0 0
// 1 0 0
// 1 0 3

前の例では、element 参照変数は、最初の配列要素の別名として初期化されています。 次に、最後の配列要素を参照するように ref 再代入されています。

ref readonly ローカル変数を定義できます。 ref readonly 変数に値を代入することはできません。 ただし、次の例に示すように、このような参照変数を ref 再代入することはできます。

int[] xs = [1, 2, 3];

ref readonly int element = ref xs[0];
// element = 100;  error CS0131: The left-hand side of an assignment must be a variable, property or indexer
Console.WriteLine(element);  // output: 1

element = ref xs[^1];
Console.WriteLine(element);  // output: 3

次の例に示すように、参照戻り値を参照変数に代入できます。

using System;

public class NumberStore
{
    private readonly int[] numbers = [1, 30, 7, 1557, 381, 63, 1027, 2550, 511, 1023];

    public ref int GetReferenceToMax()
    {
        ref int max = ref numbers[0];
        for (int i = 1; i < numbers.Length; i++)
        {
            if (numbers[i] > max)
            {
                max = ref numbers[i];
            }
        }
        return ref max;
    }

    public override string ToString() => string.Join(" ", numbers);
}

public static class ReferenceReturnExample
{
    public static void Run()
    {
        var store = new NumberStore();
        Console.WriteLine($"Original sequence: {store.ToString()}");
        
        ref int max = ref store.GetReferenceToMax();
        max = 0;
        Console.WriteLine($"Updated sequence:  {store.ToString()}");
        // Output:
        // Original sequence: 1 30 7 1557 381 63 1027 2550 511 1023
        // Updated sequence:  1 30 7 1557 381 63 1027 0 511 1023
    }
}

前の例では、GetReferenceToMax メソッドは returns-by-ref メソッドです。 最大値自体は返されませんが、最大値を保持する配列要素の別名である参照戻り値が返されます。 Run メソッドは、参照戻り値を max 参照変数に代入します。 次に、max に代入することで、store インスタンスの内部ストレージを更新します。 ref readonly メソッドを定義することもできます。 ref readonly メソッドの呼び出し元は、その参照戻り値に値を代入することはできません。

foreach ステートメントの反復変数は参照変数にすることができます。 詳細については、反復ステートメントに関する記事の「foreach ステートメント」セクションをご覧ください。

パフォーマンスが重要なシナリオでは、参照変数と戻り値を使用すると、コストのかかる可能性のあるコピー操作を回避することでパフォーマンスが向上することがあります。

コンパイラは、参照変数がそのリファレントより長く存在しないように、またその有効期間全体にわたって有効なままであるようにします。 詳細については、C# 言語仕様ref-safe-context に関するセクションを参照してください。

ref フィールドの詳細については、「ref 構造体型」の「ref フィールド」セクションを参照してください。

scoped ref

コンテキスト キーワード scoped は、値の有効期間を制限します。 scoped 修飾子は、ref-safe-to-escape または safe-to-escape 有効期間をそれぞれ現在のメソッドに制限します。 実際には、scoped 修飾子を追加すると、コードによって変数の有効期間が延長されないというアサートが行われます。

パラメーターまたはローカル変数に scoped を適用できます。 scoped 修飾子は、型が ref struct の場合にパラメーターとローカルに適用されます。 それ以外の場合、scoped 修飾子は、ローカルの参照変数にのみ適用できます。 これには、ref 修飾子で宣言されたローカル変数と、inref、または out 修飾子で宣言されたパラメーターが含まれます。

scoped 修飾子は、型が ref struct の場合は、structout パラメーター、および ref パラメーターで宣言されたメソッドの this に暗黙的に追加されます。

C# 言語仕様

詳細については、「C# 言語仕様」の次のセクションを参照してください。

scoped 修飾子の詳細については、「低レベル構造体の機能強化」の提案メモを参照してください。

関連項目