ジャンプ ステートメント - break
、continue
、return
、goto
ジャンプ ステートメントによって無条件で制御が移動します。 break
ステートメントは、これを囲む反復ステートメントまたは switch
ステートメントを終了させます。 continue
ステートメントは、これを囲む反復ステートメントの新しい反復を開始させます。 return
ステートメントは、それを含む関数の実行を終了させ、呼び出し元に制御を返します。 goto
ステートメントは、ラベルでマークされているステートメントに制御を移動します。
例外をスローし、かつ、無条件で制御を移動させる throw
ステートメントに関する詳細については、「例外処理ステートメント」記事の「throw
ステートメント」セクションを参照してください。
break
ステートメント
break
ステートメントはこれを囲む反復ステートメント (for
、foreach
、while
または 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
ステートメントは、これを囲む反復ステートメント (for
、foreach
、while
または 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 戻り値) は、メソッドから呼び出し元に参照渡しで返される値です。 つまり、呼び出し元はメソッドによって返される値を変更することができ、その変更は呼び出されたメソッド内のオブジェクトの状態に反映されます。 これを行うには、次の例に示すように、 ref
キーワードと共に return
ステートメントを使用します:
int[] 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 ローカルと呼ばれる新しい変数を作成できます。 "参照戻り値" とは、メソッドが何らかの変数への "参照" (または別名) を返すことを意味します。 その変数のスコープには、メソッドが含まれている必要があります。 その変数の有効期間は、メソッドから戻った後まで継続している必要があります。 呼び出し元によるメソッドの戻り値の変更は、メソッドによって返される変数に対して行われます。
メソッドが参照戻り値を返すという宣言があれば、それはそのメソッドが変数にエイリアスを返すことを示します。 設計の意図は、多くの場合、変更などを行うときに、呼び出し元のコードにはエイリアス経由でその変数にアクセスさせるというものです。 参照渡しで返すメソッドの戻り値の型を void
にすることはできません。
呼び出し元がオブジェクトの状態を変更するには、参照戻り値を、参照変数として明示的に定義された変数に格納する必要があります。
ref
戻り値は、呼び出されるメソッドのスコープで別の変数のエイリアスになります。 ref 戻り値の使用は、それが別名を与える変数の使用として解釈できます。
- その値を割り当てるときに、それが別名を与える変数に値を割り当てることになります。
- その値を読み取るときに、それが別名を与える変数の値を読み取ることになります。
- それを "参照渡し" で返す場合、その同じ変数に別名を返すことになります。
- それを "参照渡し" で別のメソッドに渡す場合、それが別名を与える変数への参照を渡すことになります。
- ref ローカルをエイリアスにすると、同じ変数に新しいエイリアスが作られます。
ref 戻り値は、呼び出し元のメソッドに対する ref-safe-context である必要があります。 これは次のことを意味します。
- 戻り値の有効期間は、メソッドの実行より長くならないようにする必要があります。 言い換えると、それを返すメソッドのローカル変数にすることはできません。 クラスのインスタンスまたは静的フィールドを戻り値にすることができます。または、メソッドに渡される引数を戻り値にすることもできます。 ローカル変数を返そうとすると、コンパイラ エラー CS8168 "ローカル変数 'obj' は ref ローカル変数ではないため、参照渡しで返すことはできません" が生成されます。
- 戻り値はリテラル
null
にすることができません。 ref 戻り値があるメソッドでは、現在の値がnull
(インスタンス化されていない) 値か、値の型が null 許容値型の変数にエイリアスを返すことができます。 - 戻り値は、定数、列挙型のメンバー、プロパティの値渡し戻り値、
class
またはstruct
のメソッドにすることはできません。
さらに、参照戻り値は非同期メソッドでは許可されません。 非同期メソッドは実行が終了する前に戻る可能性があり、戻り値はまだ不明です。
''参照戻り値'' を返すメソッドは次のようなものである必要があります。
これらの条件を満たし、p
という名前の Person
オブジェクトへの参照を返すメソッドを、次の例に示します。
public ref Person GetContactInformation(string fname, string lname)
{
// ...method implementation...
return ref p;
}
次に、メソッド シグネチャとメソッド本体の両方を示す、より完全な 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
参照変数に値を格納することで、返された値のコピーを回避できます。
次の例は、Title
と Author
という 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
goto
ステートメント
次の例に示すように、goto
ステートメントは、ラベルにマークされているステートメントに制御を移動します。
var matrices = new Dictionary<string, int[][]>
{
["A"] =
[
[1, 2, 3, 4],
[4, 3, 2, 1]
],
["B"] =
[
[5, 6, 7, 8],
[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 CoffeeChoice
{
Plain,
WithMilk,
WithIceCream,
}
public class GotoInSwitchExample
{
public static void Main()
{
Console.WriteLine(CalculatePrice(CoffeeChoice.Plain)); // output: 10.0
Console.WriteLine(CalculatePrice(CoffeeChoice.WithMilk)); // output: 15.0
Console.WriteLine(CalculatePrice(CoffeeChoice.WithIceCream)); // output: 17.0
}
private static decimal CalculatePrice(CoffeeChoice choice)
{
decimal price = 0;
switch (choice)
{
case CoffeeChoice.Plain:
price += 10.0m;
break;
case CoffeeChoice.WithMilk:
price += 5.0m;
goto case CoffeeChoice.Plain;
case CoffeeChoice.WithIceCream:
price += 7.0m;
goto case CoffeeChoice.Plain;
}
return price;
}
}
switch
ステートメント内では、ステートメント goto default;
を使用して、default
ラベルを持つ switch セクションに制御を移動することもできます。
指定した名前のラベルが現在の関数メンバーに存在しない場合、または goto
ステートメントがラベルのスコープ内にない場合は、コンパイル時エラーが発生します。 つまり、goto
ステートメントを使用し、現在の関数メンバーから、または入れ子になったスコープに制御を移動することはできません。
C# 言語仕様
詳細については、「C# 言語仕様」の次のセクションを参照してください。
関連項目
.NET