ジャンプ ステートメント (C# リファレンス)

次のステートメントは無条件で制御を移動します。

例外をスローし、無条件で制御を移動する throw ステートメントの詳細については、throw を参照してください。

break ステートメント

break ステートメントはこれを囲む反復ステートメント (forforeachwhile または do ループ) または switch ステートメントを終了させます。 break ステートメントは、終了したステートメントの次のステートメント (存在する場合) に制御を移動します。

int[] numbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
foreach (int number in numbers)
{
    if (number == 3)
    {
        break;
    }

    Console.Write($"{number} ");
}
Console.WriteLine();
Console.WriteLine("End of the example.");
// Output:
// 0 1 2 
// End of the example.

次の例に示すように、入れ子になったループでは、break ステートメントはそれを含む最も内側のループのみを終了します。

for (int outer = 0; outer < 5; outer++)
{
    for (int inner = 0; inner < 5; inner++)
    {
        if (inner > outer)
        {
            break;
        }

        Console.Write($"{inner} ");
    }
    Console.WriteLine();
}
// Output:
// 0
// 0 1
// 0 1 2
// 0 1 2 3
// 0 1 2 3 4

ループ内で switch ステートメントを使用すると、switch セクションの最後にある break ステートメントは、switch ステートメントからのみ制御を移動します。 次の例に示すように、switch ステートメントを含むループは影響を受けません。

double[] measurements = { -4, 5, 30, double.NaN };
foreach (double measurement in measurements)
{
    switch (measurement)
    {
        case < 0.0:
            Console.WriteLine($"Measured value is {measurement}; too low.");
            break;

        case > 15.0:
            Console.WriteLine($"Measured value is {measurement}; too high.");
            break;

        case double.NaN:
            Console.WriteLine("Failed measurement.");
            break;

        default:
            Console.WriteLine($"Measured value is {measurement}.");
            break;
    }
}
// Output:
// Measured value is -4; too low.
// Measured value is 5.
// Measured value is 30; too high.
// Failed measurement.

continue ステートメント

次の例に示すように、continue ステートメントは、これを囲む反復ステートメント (forforeachwhile または do ループ) の新しい反復を開始します。

for (int i = 0; i < 5; i++)
{
    Console.Write($"Iteration {i}: ");
    
    if (i < 3)
    {
        Console.WriteLine("skip");
        continue;
    }
    
    Console.WriteLine("done");
}
// Output:
// Iteration 0: skip
// Iteration 1: skip
// Iteration 2: skip
// Iteration 3: done
// Iteration 4: done

return ステートメント

return ステートメントは、表示される関数の実行を終了し、呼び出し元に制御と関数の結果 (存在する場合) を返します。

関数メンバーが値を計算しない場合は、次の例に示すように、式を指定せずに return ステートメントを使用します。

Console.WriteLine("First call:");
DisplayIfNecessary(6);

Console.WriteLine("Second call:");
DisplayIfNecessary(5);

void DisplayIfNecessary(int number)
{
    if (number % 2 == 0)
    {
        return;
    }

    Console.WriteLine(number);
}
// Output:
// First call:
// Second call:
// 5

前の例で示したように、通常は、式を指定せずに return ステートメントを使用して、関数メンバーを早期に終了させます。 関数メンバーに return ステートメントが含まれていない場合は、最後のステートメントが実行された後に終了します。

関数メンバーが値を計算する場合は、次の例に示すように、式を指定して return ステートメントを使用します。

double surfaceArea = CalculateCylinderSurfaceArea(1, 1);
Console.WriteLine($"{surfaceArea:F2}"); // output: 12.57

double CalculateCylinderSurfaceArea(double baseRadius, double height)
{
    double baseArea = Math.PI * baseRadius * baseRadius;
    double sideArea = 2 * Math.PI * baseRadius * height;
    return 2 * baseArea + sideArea;
}

return ステートメントに式が含まれている場合、その式は、非同期でない限り関数メンバーの戻り値の型に暗黙的に変換可能である必要があります。 async 関数から返される式は、Task<TResult> または ValueTask<TResult> の型引数に暗黙的に変換できる必要があります。どちらの場合も、関数の戻り値の型になります。 async 関数の戻り値の型が Task または ValueTask の場合は、式を指定せずに return ステートメントを使用します。

既定では、return ステートメントは式の値を返します。 変数への参照を返すことができます。 これを行うには、次の例に示すように、ref キーワードを指定して return ステートメントを使用します。

var xs = new int[] { 10, 20, 30, 40 };
ref int found = ref FindFirst(xs, s => s == 30);
found = 0;
Console.WriteLine(string.Join(" ", xs));  // output: 10 20 0 40

ref int FindFirst(int[] numbers, Func<int, bool> predicate)
{
    for (int i = 0; i < numbers.Length; i++)
    {
        if (predicate(numbers[i]))
        {
            return ref numbers[i];
        }
    }
    throw new InvalidOperationException("No element satisfies the given condition.");
}

参照戻し

戻り値は参照渡しで返すことができます (ref 戻り値)。 参照戻り値を使用すると、メソッドから呼び出し元に、値ではなく、変数への参照を返すことができます。 呼び出し元は、返された変数を値渡しとして処理するか、参照渡しとして処理するかを選択できます。 呼び出し元は、それ自体が戻り値への参照である、ref ローカルと呼ばれる新しい変数を作成できます。 "参照戻り値" とは、メソッドが何らかの変数への "参照" (または別名) を返すことを意味します。 その変数のスコープには、メソッドが含まれている必要があります。 その変数の有効期間は、メソッドから戻った後まで継続している必要があります。 呼び出し元によるメソッドの戻り値の変更は、メソッドによって返される変数に対して行われます。

メソッドが参照戻り値を返すという宣言があれば、それはそのメソッドが変数にエイリアスを返すことを示します。 設計の意図は、多くの場合、変更などを行うときに、呼び出し元のコードにはエイリアス経由でその変数にアクセスさせるというものです。 参照渡しで返すメソッドの戻り値の型を void にすることはできません。

ref 戻り値は、呼び出されるメソッドのスコープで別の変数のエイリアスになります。 ref 戻り値の使用は、それが別名を与える変数の使用として解釈できます。

  • その値を割り当てるときに、それが別名を与える変数に値を割り当てることになります。
  • その値を読み取るときに、それが別名を与える変数の値を読み取ることになります。
  • それを "参照渡し" で返す場合、その同じ変数に別名を返すことになります。
  • それを "参照渡し" で別のメソッドに渡す場合、それが別名を与える変数への参照を渡すことになります。
  • ref ローカルをエイリアスにすると、同じ変数に新しいエイリアスが作られます。

ref 戻り値は、呼び出し元のメソッドに対する ref_safe_to_escape である必要があります。 これは次のことを意味します。

  • 戻り値の有効期間は、メソッドの実行より長くならないようにする必要があります。 言い換えると、それを返すメソッドのローカル変数にすることはできません。 クラスのインスタンスまたは静的フィールドを戻り値にすることができます。または、メソッドに渡される引数を戻り値にすることもできます。 ローカル変数を返そうとすると、コンパイラ エラー CS8168 "ローカル変数 'obj' は ref ローカル変数ではないため、参照渡しで返すことはできません" が生成されます。
  • 戻り値はリテラル null にすることができません。 ref 戻り値があるメソッドでは、現在の値が null (インスタンス化されていない) 値か、値の型が null 許容値型の変数にエイリアスを返すことができます。
  • 戻り値は、定数、列挙型のメンバー、プロパティの値渡し戻り値、class または struct のメソッドにすることはできません。

さらに、参照戻り値は非同期メソッドでは許可されません。 非同期メソッドは実行が終了する前に戻る可能性があり、戻り値はまだ不明です。

''参照戻り値'' を返すメソッドは次のようなものである必要があります。

  • 戻り値の型の前に ref キーワードが含まれる。
  • メソッド本体の各 return ステートメントで、返されるインスタンスの名前の前に ref キーワードが含まれること。

これらの条件を満たし、p という名前の Person オブジェクトへの参照を返すメソッドを、次の例に示します。

public ref Person GetContactInformation(string fname, string lname)
{
    // ...method implementation...
    return ref p;
}

goto ステートメント

次の例に示すように、goto ステートメントは、ラベルにマークされているステートメントに制御を移動します。

var matrices = new Dictionary<string, int[][]>
{
    ["A"] = new[]
    {
        new[] { 1, 2, 3, 4 },
        new[] { 4, 3, 2, 1 }
    },
    ["B"] = new[]
    {
        new[] { 5, 6, 7, 8 },
        new[] { 8, 7, 6, 5 }
    },
};

CheckMatrices(matrices, 4);

void CheckMatrices(Dictionary<string, int[][]> matrixLookup, int target)
{
    foreach (var (key, matrix) in matrixLookup)
    {
        for (int row = 0; row < matrix.Length; row++)
        {
            for (int col = 0; col < matrix[row].Length; col++)
            {
                if (matrix[row][col] == target)
                {
                    goto Found;
                }
            }
        }
        Console.WriteLine($"Not found {target} in matrix {key}.");
        continue;

    Found:
        Console.WriteLine($"Found {target} in matrix {key}.");
    }
}
// Output:
// Found 4 in matrix A.
// Not found 4 in matrix B.

前の例で示したように、goto ステートメントを使用して、入れ子になったループから抜け出すことができます。

ヒント

入れ子になったループを使用する場合は、別のメソッドに個別のループをリファクタリングすることを検討してください。 これにより、goto ステートメントを使用せずに、より簡単で読みやすいコードが発生する可能性があります。

次の例に示すように、switch ステートメントgoto ステートメントを使用して、定数ケース ラベルを持つ switch セクションに制御を移動することもできます。

using System;

public enum CoffeChoice
{
    Plain,
    WithMilk,
    WithIceCream,
}

public class GotoInSwitchExample
{
    public static void Main()
    {
        Console.WriteLine(CalculatePrice(CoffeChoice.Plain));  // output: 10.0
        Console.WriteLine(CalculatePrice(CoffeChoice.WithMilk));  // output: 15.0
        Console.WriteLine(CalculatePrice(CoffeChoice.WithIceCream));  // output: 17.0
    }

    private static decimal CalculatePrice(CoffeChoice choice)
    {
        decimal price = 0;
        switch (choice)
        {
            case CoffeChoice.Plain:
                price += 10.0m;
                break;

            case CoffeChoice.WithMilk:
                price += 5.0m;
                goto case CoffeChoice.Plain;

            case CoffeChoice.WithIceCream:
                price += 7.0m;
                goto case CoffeChoice.Plain;
        }
        return price;
    }
}

switch ステートメント内では、ステートメント goto default; を使用して、default ラベルを持つ switch セクションに制御を移動することもできます。

指定した名前のラベルが現在の関数メンバーに存在しない場合、または goto ステートメントがラベルのスコープ内にない場合は、コンパイル時エラーが発生します。 つまり、goto ステートメントを使用して、現在の関数メンバーから、または入れ子になったスコープ (try ブロックなど) に制御を移動することはできません。

C# 言語仕様

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

関連項目