宣言ステートメント

宣言ステートメントは新しい変数を宣言し、必要に応じて初期化します。 すべての変数で型が宣言されています。 型の詳細については、 .NET 型システムの記事を参照してください。

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

C# 3 以降、メソッド スコープで宣言された変数には暗黙的な "型" var を与えることができます。 暗黙的に型指定されたローカル変数では、型を自分で宣言した場合と同様に厳密に型指定されますが、コンパイラが型を決定します。 次の 2 つの a の宣言は機能的に等しくなります。

var a = 10; // Implicitly typed.
int b = 10; // Explicitly typed.

重要

varnull 許容参照型を有効にして使用すると、式の型で null 値が許容されない場合でも、常に null 値を許容する参照型を表します。 コンパイラの null 状態分析により、潜在的な null 値の逆参照から保護されます。 null である可能性がある式に変数が割り当てられない場合、コンパイラから警告は生成されません。 null である可能性がある式に変数が割り当てる場合、警告が生成されないように、逆参照の前に null でないことをテストする必要があります。

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

var xs = new List<int>();

C# 9.0 以降では、代替としてターゲット型の newを使用できます。

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

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

次の例は、2 つのクエリ式を示しています。 最初の式では、var の使用が許可されますが、必須ではありません。クエリ結果の型を IEnumerable<string> として明示的に指定できるためです。 一方、2 つ目の式の var では、匿名型のコレクションとしての結果が許され、その型の名前にはコンパイラ自体以外はアクセスできません。 var を使うと、結果のために新しいクラスを作成する必要がなくなります。 例 #2 では、foreach 繰り返し変数 item も暗黙的に型指定する必要があります。

// Example #1: var is optional when
// the select clause specifies a string
string[] words = { "apple", "strawberry", "grape", "peach", "banana" };
var wordQuery = from word in words
                where word[0] == 'g'
                select word;

// Because each element in the sequence is a string,
// not an anonymous type, var is optional here also.
foreach (string s in wordQuery)
{
    Console.WriteLine(s);
}

// Example #2: var is required because
// the select clause specifies an anonymous type
var custQuery = from cust in customers
                where cust.City == "Phoenix"
                select new { cust.Name, cust.Phone };

// var must be used because each item
// in the sequence is an anonymous type
foreach (var item in custQuery)
{
    Console.WriteLine("Name={0}, Phone={1}", item.Name, item.Phone);
}

ref ローカル変数

ローカルを宣言するには、 ref 変数の型の前にキーワードを ref 追加します。 GetContactInformation メソッドが ref 戻り値として宣言されているとします。

public ref Person GetContactInformation(string fname, string lname)

値渡し代入によって、変数の値が読み取られ、それが新しい変数に渡されます。

Person p = contacts.GetContactInformation("Brandie", "Best");

先の代入では、ローカル変数として p が宣言されています。 その初期値は、GetContactInformation によって返された値の読み取りからコピーされます。 今後、p に値が代入されることで、GetContactInformation によって返された変数の値が変わることはありません。 変数 p は、返された変数のエイリアスではなくなります。

ref ローカル変数を宣言し、元の値にエイリアスをコピーします。 次の代入では、p は、GetContactInformation から返された変数のエイリアスになります。

ref Person p = ref contacts.GetContactInformation("Brandie", "Best");

この後 p を使用することは、GetContactInformation によって返された変数を使用することと同じです。p はその変数のエイリアスであるためです。 p を変更すると、GetContactInformation から返される変数も変更されます。

同じ方法で、参照渡しの値にアクセスできます。 場合によっては、参照渡しの値へのアクセスによって負荷がかかる可能性があるコピー操作が回避され、パフォーマンスが向上します。 たとえば、次のステートメントは、値の参照に使用される ref ローカル値をどのように定義できるかを示しています。

ref VeryLargeStruct reflocal = ref veryLargeStruct;

ref キーワードは、ローカル変数宣言の前 "および" 2 番目の例の値の前で使用します。 両方の例の、変数宣言と代入の両方の ref キーワードを含めないと、コンパイラ エラー CS8172 "値を使用して参照渡し変数を初期化することはできません" が生成されます。

ref VeryLargeStruct reflocal = ref veryLargeStruct; // initialization
refLocal = ref anotherVeryLargeStruct; // reassigned, refLocal refers to different storage.

ref ローカル変数は、宣言時にやはり初期化する必要があります。

次の例では、整数値の配列を格納する NumberStore クラスを定義しています。 FindNumber メソッドは、引数として渡された数値に等しいかそれより大きい最初の数値を参照渡しで返します。 引数に等しいかそれより大きい数値がない場合、メソッドはインデックス 0 の数値を返します。

using System;

class NumberStore
{
    int[] numbers = { 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023 };

    public ref int FindNumber(int target)
    {
        for (int ctr = 0; ctr < numbers.Length; ctr++)
        {
            if (numbers[ctr] >= target)
                return ref numbers[ctr];
        }
        return ref numbers[0];
    }

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

次の例では、NumberStore.FindNumber メソッドを呼び出して、16 に等しいかそれより大きい最初の値を取得します。 呼び出し元は、メソッドによって返された値を 2 倍にします。 次の例の出力では、NumberStore インスタンスの配列要素の値に変更が反映されたことが示されています。

var store = new NumberStore();
Console.WriteLine($"Original sequence: {store.ToString()}");
int number = 16;
ref var value = ref store.FindNumber(number);
value *= 2;
Console.WriteLine($"New sequence:      {store.ToString()}");
// The example displays the following output:
//       Original sequence: 1 3 7 15 31 63 127 255 511 1023
//       New sequence:      1 3 7 15 62 63 127 255 511 1023

参照戻り値がサポートされていない場合、このような操作は、配列要素のインデックスと値を返すことによって実行されます。 呼び出し元はこのインデックスを使用して、別のメソッド呼び出しで値を変更できます。 一方、インデックスを変更して配列の他の値にアクセスし、変更することも可能です。

次の例では、C# 7.3 以降で ref ローカル変数の再割り当てを使用するように FindNumber メソッドを書き直す方法を示します。

using System;

class NumberStore
{
    int[] numbers = { 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023 };

    public ref int FindNumber(int target)
    {
        ref int returnVal = ref numbers[0];
        var ctr = numbers.Length - 1;
        while ((ctr >= 0) && (numbers[ctr] >= target))
        {
            returnVal = ref numbers[ctr];
            ctr--;
        }
        return ref returnVal;
    }

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

この 2 番目のバージョンは、検索対象の数字が配列の末尾に近いシナリオで、シーケンスが長い場合に、より効率的です。配列は末尾から先頭に向かって反復処理されるので、検査される項目が少なくて済むためです。

コンパイラは、変数 (型のローカル、パラメーター、refフィールド) refref対してスコープ規則をrefref struct適用します。 この規則により、参照が参照先のオブジェクトよりも長く生きないようにします。 メソッド パラメーターに関する記事のスコープ規則に関するセクションを参照してください。

readonly ref と readonly struct

修飾子はreadonly、ローカル変数とrefフィールドにref適用できます。 修飾子は readonly 、式の右側に影響します。 次の宣言例を参照してください。

ref readonly int aConstant; // aConstant can't be value-reassigned.
readonly ref int Storage; // Storage can't be ref-reassigned.
readonly ref readonly int CantChange; // CantChange can't be value-reassigned or ref-reassigned.
  • 値の再割り当ては 、変数の値が再割り当てされたことを意味します。
  • ref 代入 は、変数が別のオブジェクトを参照することを意味します。

宣言はreadonly refreadonly ref readonlyrefref struct.

スコープ付き ref

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

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

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

関連項目