13.1 全般
C# には、さまざまなステートメントが用意されています。
注: これらのステートメントのほとんどは、C および C++ でプログラミングした開発者にはなじみがあります。 注釈
statement
: labeled_statement
| declaration_statement
| embedded_statement
;
embedded_statement
: block
| empty_statement
| expression_statement
| selection_statement
| iteration_statement
| jump_statement
| try_statement
| checked_statement
| unchecked_statement
| lock_statement
| using_statement
| yield_statement
| unsafe_statement // unsafe code support
| fixed_statement // unsafe code support
;
unsafe_statement (§24.2) と fixed_statement (§24.7) は、安全でないコード (§24) でのみ使用できます。
embedded_statement非終端は、他のステートメント内に表示されるステートメントに使用されます。 ステートメントではなくembedded_statementを使用するとこれらのコンテキストでの宣言ステートメントとラベル付きステートメントの使用は除外されます。
例: コード
void F(bool b) { if (b) int i = 44; }
ifステートメントでは、 分岐に statement ではなくifが必要なため、コンパイル時エラーが発生します。 このコードが許可されている場合、変数iが宣言されますが、使用することはできませんでした。 ただし、iの宣言をブロックに配置することで、この例は有効であることに注意してください。終了サンプル
13.2 エンドポイントと到達可能性
すべてのステートメントには、 エンドポイントがあります。 直感的に言えば、ステートメントの終点は、ステートメントの直後の場所です。 複合ステートメント (埋め込みステートメントを含むステートメント) の実行規則では、コントロールが埋め込みステートメントの終点に達したときに実行されるアクションを指定します。
例: コントロールがブロック内のステートメントの終点に達すると、コントロールはブロック内の次のステートメントに転送されます。 終了サンプル
実行によってステートメントに到達できる可能性がある場合、ステートメントは 到達可能であると言われます。 逆に、ステートメントが実行される可能性がない場合、ステートメントは 到達不能であると言われます。
例: 次のコード内
void F() { Console.WriteLine("reachable"); goto Label; Console.WriteLine("unreachable"); Label: Console.WriteLine("reachable"); }ステートメントが実行される可能性がないため、Console.WriteLine の 2 回目の呼び出しに到達できません。
終了サンプル
throw_statement、block、またはempty_statement以外のステートメントに到達できない場合、警告が報告されます。 明示的に、ステートメントに到達できないこと自体はエラーではありません。
注意: 特定のステートメントまたはエンドポイントに到達できるかどうかを判断するために、コンパイラは、各ステートメントに定義されている到達可能性ルールに従ってフロー分析を実行します。 フロー分析では、ステートメントの動作を制御する定数式 (§12.25) の値が考慮されますが、非定数式の可能な値は考慮されません。 つまり、制御フロー分析のために、特定の型の非定数式は、その型の任意の値を持つと見なされます。
この例では、
void F() { const int i = 1; if (i == 2) Console.WriteLine("unreachable"); }
if演算子の両方のオペランドが定数であるため、==ステートメントのブール式は定数式です。 定数式はコンパイル時に評価され、false値が生成されるため、Console.WriteLine呼び出しは到達不能と見なされます。 ただし、iがローカル変数に変更された場合void F() { int i = 1; if (i == 2) Console.WriteLine("reachable"); }
Console.WriteLine呼び出しは到達可能と見なされますが、実際には実行されません。注釈
関数メンバーまたは匿名関数の ブロック は常に到達可能と見なされます。 ブロック内の各ステートメントの到達可能性ルールを連続して評価することで、特定のステートメントの到達可能性を判断できます。
例: 次のコード内
void F(int x) { Console.WriteLine("start"); if (x < 0) Console.WriteLine("negative"); }2 番目の
Console.WriteLineの到達可能性は次のように決定されます。
Console.WriteLineメソッドのブロックに到達可能であるため、最初のF式ステートメントに到達できます (§13.3)。- 最初の
Console.WriteLine式ステートメントのエンドポイントに到達可能なのは、そのステートメントが到達可能であるためです (§13.7 および §13.3)。- 最初の
if式ステートメントのエンドポイントに到達できるため、Console.WriteLineステートメントに到達できます (§13.7 および §13.3)。Console.WriteLineステートメントのブール式に定数値ifがないため、2 番目のfalse式ステートメントに到達できます。終了サンプル
ステートメントのエンドポイントに到達できるコンパイル時エラーは、次の 2 つの状況で発生します。
switchステートメントでは、switch セクションが次の switch セクションに "フォール スルー" することは許可されないため、それは switch セクションのステートメント一覧の終了ポイントに到達できるコンパイル時エラーになります。 このエラーが発生した場合は、通常、breakステートメントが見つからないことを示します。これは、到達可能な値を計算する関数メンバーまたは匿名関数のブロックのエンドポイントのコンパイル時エラーです。 このエラーが発生した場合、通常は
returnステートメントが見つからないことを示します (§13.10.5)。
13.3 ブロック
13.3.1 全般
"ブロック" を使用すると、1 つのステートメントしか使用できないコンテキストで複数のステートメントを記述できます。
block
: '{' statement_list? '}'
;
block は、オプションの statement_list (§13.3.2) で構成され、中かっこで囲まれています。 ステートメント リストを省略すると、ブロックは空と見なされます。
ブロックには宣言ステートメント (§13.6) を含められることがあります。 ブロックで宣言されたローカル変数または定数のスコープはブロックです。
ブロックは次のように実行されます。
- ブロックが空の場合、制御はブロックの終点に転送されます。
- ブロックが空でない場合は、制御がステートメント・リストに転送されます。 コントロールがステートメント リストの終点に達すると、コントロールはブロックの終点に転送されます。
ブロック自体に到達可能な場合、ブロックのステートメント リストに到達できます。
ブロックが空の場合、またはステートメント リストの終点に到達可能な場合、ブロックの終点に到達できます。
1 つ以上の ステートメント (yield) を含むブロックは反復子ブロックと呼ばれます。 反復子ブロックは、関数メンバーを反復子として実装するために使用されます (§15.15)。 反復子ブロックには、いくつかの追加の制限が適用されます。
- これは、
returnステートメントが反復子ブロックに表示されるコンパイル時エラーです (ただし、yield returnステートメントは許可されています)。 - 反復子ブロックに安全でないコンテキスト (§24.2) が含まれる場合のコンパイル時エラーです。 反復子ブロックは、安全でないコンテキストで宣言が入れ子になっている場合でも、常に安全なコンテキストを定義します。
13.3.2 ステートメントリスト
ステートメント リストは、順番に記述された 1 つ以上のステートメントで構成されます。 ステートメント リストは、 blocks (§13.3) と switch_block (§13.8.3) で発生します。
statement_list
: statement+
;
ステートメント リストは、コントロールを最初のステートメントに転送することによって実行されます。 コントロールがステートメントの終点に達すると、コントロールは次のステートメントに転送されます。 コントロールが最後のステートメントの終点に達した場合、コントロールはステートメント リストのエンドポイントに転送されます。
次のうち少なくとも 1 つが当てはまる場合、ステートメント リスト内のステートメントに到達できます。
- ステートメントは最初のステートメントであり、ステートメント・リスト自体に到達可能です。
- 前のステートメントのエンドポイントに到達可能です。
- ステートメントはラベル付きステートメントであり、ラベルは到達可能な
gotoステートメントによって参照されます。
リスト内の最後のステートメントの終点に到達可能な場合、ステートメント リストの終点に到達できます。
13.4 空のステートメント
empty_statementでは何も行われません。
empty_statement
: ';'
;
空のステートメントは、ステートメントが必要なコンテキストで実行する操作がない場合に使用されます。
空のステートメントを実行すると、単にステートメントのエンドポイントに制御が転送されます。 したがって、空のステートメントに到達できる場合、そのステートメントのエンドポイントにも到達できます。
例: null 本文を含む
whileステートメントを記述するときに、空のステートメントを使用できます。bool ProcessMessage() {...} void ProcessMessages() { while (ProcessMessage()) ; }また、空のステートメントを使用して、ブロックの終了 "
}" の直前にラベルを宣言できます。void F(bool done) { ... if (done) { goto exit; } ... exit: ; }終了サンプル
13.5 ラベル付きステートメント
labeled_statementでは、ステートメントの先頭にラベルを付けます。 ラベル付きステートメントはブロックで許可されますが、埋め込みステートメントとしては使用できません。
labeled_statement
: identifier ':' statement
;
ラベル付きステートメントは、 identifierによって指定された名前を持つラベルを宣言します。 ラベルのスコープは、入れ子になったブロックを含め、ラベルが宣言されているブロック全体です。 同じ名前の 2 つのラベルに重複するスコープがあると、コンパイル時エラーになります。
ラベルは、 goto ステートメント (§13.10.4) からラベルのスコープ内で参照できます。
注: つまり、
gotoステートメントは、ブロック内およびブロック外で制御を転送できますが、ブロックに転送することはできません。 注釈
ラベルには独自の宣言領域があり、他の識別子に干渉することはありません。
例: 例
int F(int x) { if (x >= 0) { goto x; } x = -x; x: return x; }は有効であり、パラメーターとラベルの両方として x という名前を使用します。
終了サンプル
ラベル付きステートメントの実行は、ラベルに続くステートメントの実行に正確に対応します。
通常の制御フローによって提供される到達可能性に加えて、ラベル付きステートメントが到達可能なgoto ステートメントによって参照されている場合、ラベル付きステートメントは到達可能です。ただし、goto ステートメントがtry ブロック内にあるか、エンドポイントに到達できないcatch ブロックを含むtry_statementのfinally ブロック内にあり、ラベル付きステートメントがtry_statement外にある場合を除きます。
13.6 宣言ステートメント
13.6.1 全般
declaration_statementは、1 つ以上のローカル変数、1 つ以上のローカル定数、またはローカル関数を宣言します。 宣言ステートメントはブロックおよび switch ブロックで許可されますが、埋め込みステートメントとしては許可されません。
declaration_statement
: local_variable_declaration ';'
| local_constant_declaration ';'
| local_function_declaration
;
ローカル変数は、 local_variable_declaration (§13.6.2) を使用して宣言されます。 ローカル定数は、 local_constant_declaration (§13.6.3) を使用して宣言されます。 ローカル関数は、 local_function_declaration (§13.6.4) を使用して宣言されます。
宣言された名前は、最も近い外側の宣言空間 (§7.3) に導入されます。
13.6.2 ローカル変数宣言
13.6.2.1 全般
local_variable_declarationは、1 つ以上のローカル変数を宣言します。
local_variable_declaration
: implicitly_typed_local_variable_declaration
| explicitly_typed_local_variable_declaration
| explicitly_typed_ref_local_variable_declaration
;
暗黙的に型指定された宣言には、コンテキスト キーワード (§6.4.4) var 含まれるため、次のように解決される 3 つのカテゴリ間で構文的なあいまいさが生じる可能性があります。
- スコープに
varという名前の型がなく、入力が implicitly_typed_local_variable_declaration 一致する場合は選択されます。 - それ以外の場合、
varという名前の型がスコープ内にある場合、 implicitly_typed_local_variable_declaration は一致するとは見なされません。
local_variable_declaration 内では、各変数は declarator によって導入されます。この宣言子は、暗黙的に型指定されたローカル変数、明示的に型指定されたローカル変数、および ref ローカル変数に対して、それぞれ implicitly_typed_local_variable_declarator、explicitly_typed_local_variable_declarator、または ref_local_variable_declarator のいずれかになります。 宣言子は、導入された変数の名前 (identifier) と初期値 (存在する場合) を定義します。
宣言に複数の宣言子がある場合は、初期化式を含め、左から右 (§9.4.4.5) で処理されます。
注: local_variable_declarationがfor_initializer (§13.9.4) またはresource_acquisition (§13.14) として発生しない場合、この左から右への順序は、各宣言子が個別のlocal_variable_declaration内にある場合と同じです。 次に例を示します。
void F() { int x = 1, y, z = x * 2; }は以下に匹敵します。
void F() { int x = 1; int y; int z = x * 2; }注釈
ローカル変数の値は、 simple_name (§12.8.4) を使用して式で取得されます。 ローカル変数は、値が取得される各場所に確実に割り当てられます (§9.4)。 local_variable_declarationによって導入された各ローカル変数は、に割り当てられていない (§9.4.3) です。 宣言子に初期化式がある場合、導入されたローカル変数は宣言子の末尾に assigned として分類されます (§9.4.4.5)。
local_variable_declarationによって導入されたローカル変数のスコープは、次のように定義されます (§7.7)。
- 宣言が for_initializer として発生する場合、スコープは for_initializer、 for_condition、 for_iterator、および embedded_statement (§13.9.4) です。
- 宣言が resource_acquisition として発生する場合、スコープは、 using_statement の意味的に等価な拡張の最も外側のブロックです (§13.14)。
- それ以外の場合、スコープは宣言が行われるブロックです。
宣言子の前のテキスト位置、または宣言子内の初期化式内で、ローカル変数を名前で参照するとエラーになります。 ローカル変数のスコープ内では、同じ名前の別のローカル変数、ローカル関数、または定数を宣言するのはコンパイル時エラーです。
ref ローカル変数の ref-safe-context (§9.7.2) は、初期化 variable_reference の ref-safe-context です。 ref 以外のローカル変数の ref-safe コンテキストは、 declaration-blockです。
13.6.2.2 暗黙的に型指定されたローカル変数宣言
implicitly_typed_local_variable_declaration
: 'var' implicitly_typed_local_variable_declarator
| ref_kind 'var' ref_local_variable_declarator
;
implicitly_typed_local_variable_declarator
: identifier '=' expression
;
implicitly_typed_local_variable_declarationでは、1 つのローカル変数 identifier が導入されます。
式またはvariable_referenceには、コンパイル時の型Tを指定する必要があります。 最初の代替方法では、expressionの初期値を持つ変数を宣言します。T?が null 非許容参照型の場合、その型はT。それ以外の場合は、その型がT。 2 番目の代替方法は、初期値が refvariable_reference の ref 変数を宣言します。ref T?が null 非許容参照型の場合、その型はT。それ以外の場合、その型はref T。 (ref_kind は、 §15.6.1 で説明されています。
例:
var i = 5; var s = "Hello"; var d = 1.0; var numbers = new int[] {1, 2, 3}; var orders = new Dictionary<int,Order>(); ref var j = ref i; ref readonly var k = ref i;上記の暗黙的に型指定されたローカル変数宣言は、次の明示的に型指定された宣言と正確に等価です。
int i = 5; string s = "Hello"; double d = 1.0; int[] numbers = new int[] {1, 2, 3}; Dictionary<int,Order> orders = new Dictionary<int,Order>(); ref int j = ref i; ref readonly int k = ref i;暗黙的に型指定されたローカル変数の宣言が正しくありません。
var x; // Error, no initializer to infer type from var y = {1, 2, 3}; // Error, array initializer not permitted var z = null; // Error, null does not have a type var u = x => x + 1; // Error, anonymous functions do not have a type var v = v++; // Error, initializer cannot refer to v itself終了サンプル
13.6.2.3 明示的に型指定されたローカル変数宣言
explicitly_typed_local_variable_declaration
: type explicitly_typed_local_variable_declarators
;
explicitly_typed_local_variable_declarators
: explicitly_typed_local_variable_declarator
(',' explicitly_typed_local_variable_declarator)*
;
explicitly_typed_local_variable_declarator
: identifier ('=' local_variable_initializer)?
;
local_variable_initializer
: expression
| array_initializer
;
explicitly_typed_local_variable_declarationでは、指定した型を持つ 1 つ以上のローカル変数が導入されます。
local_variable_initializerが存在する場合、その型は単純代入 (§12.23.2) または配列初期化 (§17.7) の規則に従って適切であり、その値は変数の初期値として割り当てられます。
13.6.2.4 明示的に型指定された ref ローカル変数の宣言
explicitly_typed_ref_local_variable_declaration
: ref_kind type ref_local_variable_declarators
;
ref_local_variable_declarators
: ref_local_variable_declarator (',' ref_local_variable_declarator)*
;
ref_local_variable_declarator
: identifier '=' 'ref' variable_reference
;
初期化 variable_reference には 型があり 、 ref 代入 の場合と同じ要件を満たす必要があります (§12.23.3)。
ref_kindがref readonly場合、宣言されている識別子は、読み取り専用として扱われる変数への参照です。 それ以外の場合、 ref_kind が refされている場合、宣言されている 識別子は書き込み可能な変数への参照になります。
method_modifierref struct で宣言されたメソッド内、または反復子 (§15.15) 内で ref ローカル変数またはasync型の変数を宣言するのはコンパイル時エラーです。
13.6.3 ローカル定数宣言
local_constant_declarationは、1 つ以上のローカル定数を宣言します。
local_constant_declaration
: 'const' type constant_declarators
;
constant_declarators
: constant_declarator (',' constant_declarator)*
;
constant_declarator
: identifier '=' constant_expression
;
local_constant_declarationの型は、宣言によって導入された定数の型を指定します。 型の後に constant_declaratorの一覧が続き、それぞれに新しい定数が導入されます。
constant_declaratorは、定数に名前を付ける識別子、その後に "=" トークン、続けて定数の値を指定するconstant_expression (§12.25) で構成されます。
ローカル定数宣言の type および constant_expression は、定数メンバー宣言 (§15.4) と同じ規則に従う必要があります。
ローカル定数の値は、 simple_name (§12.8.4) を使用して式で取得されます。
ローカル定数のスコープは、宣言が行われるブロックです。 constant_declaratorの末尾の前にあるテキスト位置のローカル定数を参照するとエラーになります。
複数の定数を宣言するローカル定数宣言は、同じ型の 1 つの定数の複数の宣言と同じです。
13.6.4 ローカル関数の宣言
local_function_declarationはローカル関数を宣言します。
local_function_declaration
: local_function_modifier* return_type local_function_header
local_function_body
| ref_local_function_modifier* ref_kind ref_return_type
local_function_header ref_local_function_body
;
local_function_header
: identifier '(' parameter_list? ')'
| identifier type_parameter_list '(' parameter_list? ')'
type_parameter_constraints_clause*
;
local_function_modifier
: ref_local_function_modifier
| 'async'
;
ref_local_function_modifier
: 'static'
| unsafe_modifier // unsafe code support
;
local_function_body
: block
| '=>' null_conditional_invocation_expression ';'
| '=>' expression ';'
;
ref_local_function_body
: block
| '=>' 'ref' variable_reference ';'
;
文法上の注意: local_function_bodyを認識するとき、null_conditional_invocation_expressionと式の両方が適用される場合は、前者が選択されます。 (§15.6.1)
例: ローカル関数には、反復子メソッドと非同期メソッドの 2 つの一般的なユース ケースがあります。 反復子メソッドの場合、例外が検出されるのは、返されたシーケンスを列挙するコードを呼び出した場合のみです。 非同期メソッドでは、返されたタスクが待機されている場合にのみ例外が観察されます。 次の例では、ローカル関数を使用し、反復子の実装からパラメーター検証を分ける動作を確認できます。
public static IEnumerable<char> AlphabetSubset(char start, char end) { if (start < 'a' || start > 'z') { throw new ArgumentOutOfRangeException(paramName: nameof(start), message: "start must be a letter"); } if (end < 'a' || end > 'z') { throw new ArgumentOutOfRangeException(paramName: nameof(end), message: "end must be a letter"); } if (end <= start) { throw new ArgumentException( $"{nameof(end)} must be greater than {nameof(start)}"); } return AlphabetSubsetImplementation(); IEnumerable<char> AlphabetSubsetImplementation() { for (var c = start; c < end; c++) { yield return c; } } }終了サンプル
以下で特に指定しない限り、すべての文法要素のセマンティクスは、メソッドではなくローカル関数のコンテキストで読み取 method_declaration (§15.6.1) の場合と同じです。
local_function_declarationのidentifierは、外側のローカル変数宣言スペースを含め、宣言されたブロック スコープ内で一意である必要があります。 この結果の 1 つは、オーバーロードされた local_function_declarationが許可されないことです。
local_function_declarationには、1 つのasync (§15.14) 修飾子と 1 つのunsafe (§24.1) 修飾子を含めることができます。 宣言に async 修飾子が含まれている場合、戻り値の型は void または «TaskType» 型 (§15.14.1) になります。 宣言に static 修飾子が含まれている場合、関数は 静的ローカル関数です。それ以外の場合は、 非静的ローカル関数です。 これは、 type_parameter_list または parameter_list に attributes が含まれる場合のコンパイル時エラーです。 ローカル関数が安全でないコンテキスト (§24.2) で宣言されている場合、ローカル関数の宣言に unsafe 修飾子が含まれていない場合でも、ローカル関数に安全でないコードが含まれる可能性があります。
ローカル関数はブロック スコープで宣言されます。 非静的ローカル関数は、外側のスコープから変数をキャプチャすることができますが、静的ローカル関数はキャプチャすることができません(そのため、外側のローカル変数、パラメーター、非静的ローカル関数、またはthisにアクセスすることはできません)。 キャプチャされた変数が非静的ローカル関数の本体によって読み取られたが、関数の各呼び出しの前に確実に割り当てられない場合は、コンパイル時エラーです。 コンパイラは、どの変数が確実に戻り時に割り当てられるかを決定します (§9.4.4.33)。
thisの型が構造体型の場合、ローカル関数の本体がthisにアクセスするためのコンパイル時エラーです。 これは、アクセスが明示的 (this.xと同様) か暗黙的か (xxが構造体のインスタンス メンバーである場合) に当てはまります。 この規則では、このようなアクセスのみが禁止され、メンバー参照によって構造体のメンバーが作成されるかどうかには影響しません。
goto ステートメント、break ステートメント、またはターゲットがローカル関数の本体の外部にあるcontinueステートメントを含むのは、ローカル関数の本体のコンパイル時エラーです。
注: 上記の
thisおよびgoto規則は、 §12.21.3 の匿名関数の規則を反映しています。 注釈
ローカル関数は、宣言の前に字句ポイントから呼び出される場合があります。 ただし、ローカル関数で使用される変数の宣言 (§7.7) の前に、関数を構文的に宣言するのはコンパイル時エラーです。
ローカル関数が、外側のローカル変数宣言空間で宣言されたものと同じ名前のパラメーター、型パラメーター、またはローカル変数を宣言するのはコンパイル時エラーです。
ローカル関数の本体は常にアクセス可能です。 ローカル関数宣言の開始点に到達可能な場合、ローカル関数宣言のエンドポイントに到達できます。
例: 次の例では、
Lの始点に到達できない場合でも、Lの本文に到達できます。Lの始点に到達できないため、Lのエンドポイントに続くステートメントに到達できません。class C { int M() { L(); return 1; // Beginning of L is not reachable int L() { // The body of L is reachable return 2; } // Not reachable, because beginning point of L is not reachable return 3; } }つまり、ローカル関数宣言の場所は、包含関数内のステートメントの到達可能性には影響しません。 終了サンプル
ローカル関数の引数の型が dynamic場合、呼び出される関数は実行時ではなくコンパイル時に解決されます。
ローカル関数は、式ツリーでは使用できません。
静的ローカル関数
- 外側のスコープから静的メンバー、型パラメーター、定数定義、静的ローカル関数を参照できます。
- 暗黙的な
this参照からbaseまたはthisを参照したり、外側のスコープからローカル変数、パラメーター、または非静的ローカル関数を参照したりしてはなりません。 ただし、これらのすべてがnameof()式で許可されます。
13.7 式ステートメント
expression_statementは、特定の式を評価します。 式によって計算された値 (存在する場合) は破棄されます。
expression_statement
: statement_expression ';'
;
statement_expression
: null_conditional_invocation_expression
| invocation_expression
| object_creation_expression
| assignment
| post_increment_expression
| post_decrement_expression
| pre_increment_expression
| pre_decrement_expression
| await_expression
;
すべての式がステートメントとして許可されているわけではありません。
注: 特に、単に値を計算する (破棄される)
x + yやx == 1などの式は、ステートメントとして許可されません。 注釈
expression_statementの実行は、含まれている式を評価し、制御をexpression_statementのエンドポイントに転送します。 expression_statement のエンド ポイントは、その expression_statement が到達可能であれば、到達可能です。
13.8 選択ステートメント
13.8.1 全般
選択ステートメントは、いくつかの式の値に基づいて、実行できるいくつかのステートメントの 1 つを選択します。
selection_statement
: if_statement
| switch_statement
;
13.8.2 if ステートメント
if ステートメントは、ブール式の値に基づいて実行するステートメントを選択します。
if_statement
: 'if' '(' boolean_expression ')' embedded_statement
| 'if' '(' boolean_expression ')' embedded_statement
'else' embedded_statement
;
else部分は、構文で許可されている語的に最も近い前のifに関連付けられています。
例: したがって、フォームの
ifステートメントはif (x) if (y) F(); else G();上記の式は、次の式と同じです。
if (x) { if (y) { F(); } else { G(); } }終了サンプル
if ステートメントは次のように実行されます。
- boolean_expression (§12.26) が評価されます。
- ブール式が
trueを生成する場合、コントロールは最初の埋め込みステートメントに転送されます。 コントロールがそのステートメントの終点に達した場合、制御はifステートメントのエンドポイントに転送されます。 - ブール式が
falseを生成し、else部分が存在する場合は、2 番目の埋め込みステートメントに制御が転送されます。 コントロールがそのステートメントの終点に達した場合、制御はifステートメントのエンドポイントに転送されます。 - ブール式が
falseを生成し、else部分が存在しない場合は、ifステートメントのエンドポイントに制御が転送されます。
if ステートメントの最初の埋め込みステートメントは、if ステートメントが到達可能であり、かつブール式が定数値falseを持たない場合に到達可能です。
if ステートメントの 2 番目の埋め込みステートメント (存在する場合) は、if ステートメントが到達可能であり、かつブール式が定数値trueを持たない場合に到達可能です。
if ステートメントのエンドポイントは、その埋め込みステートメントの少なくとも 1 つのエンドポイントに到達可能な場合に到達できます。 さらに、if 部分がない else ステートメントのエンド ポイントは、if ステートメントが到達可能であり、ブール式が定数値 true を持たない場合に到達可能です。
13.8.3 switch ステートメント
switch ステートメントは、スイッチのselector_expressionの値に対応するスイッチ ラベルが関連付けられているステートメント リストを実行するために選択します。
switch_statement
: 'switch' selector_expression switch_block
;
selector_expression
: '(' expression ')'
| tuple_expression
;
switch_block
: '{' switch_section* '}'
;
switch_section
: switch_label+ statement_list
;
switch_label
: 'case' pattern case_guard? ':'
| 'default' ':'
;
case_guard
: 'when' null_coalescing_expression
;
switch_statementは、キーワード switchの後に、tuple_expression式またはかっこで囲まれた式 (それぞれselector_expressionと呼ばれます)、その後にswitch_blockで構成されます。
switch_blockは、0 個以上の switch_section で構成され、中かっこで囲まれています。 各 switch_section は、1 つ以上の switch_labelの後に statement_list (§13.3.2) で構成されます。
caseを含む各switch_labelには、スイッチのselector_expressionの値がテストされるパターン (§11) が関連付けられています。
case_guardが存在する場合、その式はbool型に暗黙的に変換でき、その式はケースが満たされていると見なされる追加の条件として評価されます。
注: 便宜上、selector_expressionがtuple_expressionの場合、switch_statement内のかっこは省略できます。 たとえば、
switch ((a, b)) …はswitch (a, b) …として記述できます。 注釈
switch ステートメントの管理型は、スイッチのselector_expressionによって確立されます。
- スイッチの selector_expression の型が
sbyte、byte、short、ushort、int、uint、long、ulong、char、bool、string、または enum_typeである場合、またはこれらの型のいずれかに対応する null 許容値型である場合、switchステートメントの制御型になります。 - それ以外の場合、スイッチの selector_expression の型から次のいずれかの可能なガバナンス型に対して、ユーザー定義の暗黙的な変換が 1 つだけ存在する場合:
sbyte、byte、short、ushort、int、uint、long、ulong、char、string、またはこれらの型のいずれかに対応する null 許容値型。変換された型は、switchステートメントの制御型です。 - それ以外の場合、
switchステートメントの制御型はスイッチの selector_expressionの型です。 このような型が存在しない場合はエラーです。
default ステートメントには、最大で 1 つのswitchラベルを含めることができます。
入力式の型に対してスイッチラベルのパターンが適切でない場合は、エラーです (§11.2.1)。
ケース ガードを持っていないか、ケース ガードが値 true の定数式である switch ステートメントの以前の swtich ラベルのパターン セットに、任意の switch ラベルのパターンが包含される場合 (§11.3)、エラーになります。
例:
switch (shape) { case var x: break; case var _: // error: pattern subsumed, as previous case always matches break; default: break; // warning: unreachable, all possible values already handled. }終了サンプル
switch ステートメントは次のように実行されます。
- スイッチの selector_expression が評価され、管理型に変換されます。
- 制御は、変換後のスイッチの selector_expressionの値に従って転送されます。
- スイッチのselector_expressionの値に一致し、ガード式が存在しないか true と評価される、同じ
switchステートメント内のcaseラベルのセット内の構文上の最初のパターンにより、一致したcaseラベルの後のステートメント リストに制御が転送されます。 - それ以外の場合、
defaultラベルが存在する場合は、defaultラベルの後のステートメント リストに制御が転送されます。 - それ以外の場合、制御は
switchステートメントのエンドポイントに転送されます。
- スイッチのselector_expressionの値に一致し、ガード式が存在しないか true と評価される、同じ
注: 実行時にパターンが一致する順序は定義されていません。 コンパイラは、パターンを順不同に一致させ、既に一致したパターンの結果を再利用して他のパターンの一致の結果を計算することが許可されます (必須ではありません)。 ただしコンパイラは、式に一致し、ガード句が存在しないか
trueに評価される、構文的に最初のパターンを決定する必要があります。 注釈
switch セクションのステートメント リストのエンドポイントに到達できない場合は、コンパイル時エラーが発生します。 これは"フォールスルーなし" ルールと呼ばれます。
例: 例
switch (i) { case 0: CaseZero(); break; case 1: CaseOne(); break; default: CaseOthers(); break; }スイッチセクションに到達可能なエンドポイントがないため、有効です。 C および C++ とは異なり、switch セクションの実行は、次の switch セクションに "フォールスルー" することは許可されていません。この例では、
switch (i) { case 0: CaseZero(); case 1: CaseZeroOrOne(); default: CaseAny(); }の場合、コンパイル時エラーが発生します。 switch セクションの実行の後に別の switch セクションを実行する場合は、明示的な
goto caseまたはgoto defaultステートメントを使用する必要があります。switch (i) { case 0: CaseZero(); goto case 1; case 1: CaseZeroOrOne(); goto default; default: CaseAny(); break; }終了サンプル
switch_sectionでは複数のラベルを使用できます。
例: 例
switch (i) { case 0: CaseZero(); break; case 1: CaseOne(); break; case 2: default: CaseTwo(); break; }は有効です。 ラベル
case 2:とdefault:は同じ switch_sectionの一部であるため、この例は "フォールスルーなし" ルールに違反しません。終了サンプル
注: "フォール スルーなし" ルールは、
breakステートメントが誤って省略された場合に C および C++ で発生するバグの一般的なクラスを防ぎます。 たとえば、上記のswitchステートメントのセクションは、ステートメントの動作に影響を与えずに元に戻すことができます。switch (i) { default: CaseAny(); break; case 1: CaseZeroOrOne(); goto default; case 0: CaseZero(); goto case 1; }注釈
注: switch セクションのステートメント・リストは、通常、
break、goto case、またはgoto defaultステートメントで終わりますが、ステートメント・リストの終点を到達不能にするコンストラクトは許可されます。 たとえば、ブール式whileによって制御されるtrueステートメントは、そのエンドポイントに到達しないことがわかっているとします。 同様に、throwステートメントまたはreturnステートメントは常に制御を他の場所に転送し、そのエンドポイントに到達することはありません。 したがって、次の例は有効です。switch (i) { case 0: while (true) { F(); } case 1: throw new ArgumentException(); case 2: return; }注釈
例:
switchステートメントの管理型は、string型にすることができます。 次に例を示します。void DoCommand(string command) { switch (command.ToLower()) { case "run": DoRun(); break; case "save": DoSave(); break; case "quit": DoQuit(); break; default: InvalidCommand(command); break; } }終了サンプル
注: 文字列の等値演算子 (§12.14.8) と同様に、
switchステートメントでは大文字と小文字が区別され、スイッチのselector_expression文字列がcaseラベル定数と正確に一致する場合にのみ、特定の switch セクションが実行されます。 注釈
switch ステートメントの制御型がstringまたは null 許容値型である場合、null値はcaseラベル定数として許可されます。
switch_blockのstatement_listには、宣言ステートメント (§13.6) を含める場合があります。 switch ブロックで宣言されているローカル変数または定数のスコープは、switch ブロックです。
次のうち少なくとも 1 つが当てはまる場合、スイッチ ラベルに到達できます。
- スイッチの selector_expression は定数値であり、
- スイッチの selector_expression は定数値ではなく、
- ラベルは、ガードのない
caseであるか、定数が false でないガードを持つものになります。 - それは
defaultラベルであり、- ガードを持たない、または定数が true のガードを持つ switch ステートメントのケースに出現するパターンのセットは、その制御型に対して完全ではありません (§11.4)。
- swtich を制御する型は null 許容型であり、ガードを持たない、または定数が true であるガードを持つ switch ステートメントのケースの中で出現するパターン セットには、値
nullと一致するパターンは含まれていません。
- ラベルは、ガードのない
- スイッチ ラベルは、到達可能な
goto caseまたはgoto defaultステートメントによって参照されます。
特定の switch セクションのステートメント リストは、 switch ステートメントに到達可能で、switch セクションに到達可能なスイッチ ラベルが含まれている場合に到達可能です。
switch ステートメントが到達可能であり、次の少なくとも 1 つが当てはまる場合、 switch ステートメントのエンドポイントに到達できます。
-
switchステートメントには、breakステートメントを終了する到達可能なswitchステートメントが含まれています。 -
defaultラベルが存在せず、いずれか- スイッチの selector_expression は非定数値であり、ガードがない、または値が定数 true のガードを持つ switch ステートメントのケースの中で出現するパターンのセットは、スイッチ ガバナンスの種類に関して 網羅的 なものではありません (§11.4)。
- スイッチの selector_expression は null 許容型の非定数値であり、ガードを持たない、または値が true のガードを持つ switch ステートメントの場合、パターンは
null値と一致しません。 - スイッチの selector_expression は定数値であり、ガードのない
caseラベルや、ガードが定数 true のラベルは、その値と一致しません。
例: 次のコードは、
when句の簡潔な使用を示しています。static object CreateShape(string shapeDescription) { switch (shapeDescription) { case "circle": return new Circle(2); … case var o when string.IsNullOrWhiteSpace(o): return null; default: return "invalid shape description"; } }var ケースは、
null、空の文字列、または空白のみを含む任意の文字列と一致します。 終了サンプル
13.9 反復ステートメント
13.9.1 全般
反復ステートメントは、埋め込みステートメントを繰り返し実行します。
iteration_statement
: while_statement
| do_statement
| for_statement
| foreach_statement
;
13.9.2 while ステートメント
while ステートメントは、埋め込みステートメントを 0 回以上条件付きで実行します。
while_statement
: 'while' '(' boolean_expression ')' embedded_statement
;
while ステートメントは次のように実行されます。
- boolean_expression (§12.26) が評価されます。
- ブール式が
trueを生成する場合、コントロールは埋め込みステートメントに転送されます。 コントロールが (continueステートメントの実行から) 埋め込みステートメントのエンドポイントに到達した場合、制御はwhileステートメントの先頭に転送されます。 - ブール式が
falseを生成する場合は、whileステートメントのエンドポイントに制御が転送されます。
while ステートメントの埋め込みステートメント内で、 breakステートメント (§13.10.2) を使用して、while ステートメントのエンドポイントに制御を転送できます (したがって、埋め込みステートメントの反復処理を終了します)、continueステートメント (§13.10.3) を使用して、埋め込みステートメントのエンドポイントに制御を転送できます (したがって、whileステートメントのもう 1 つの反復処理を実行します)。
while ステートメントの埋め込みステートメントに到達できるのは、while ステートメントに到達可能であり、かつブール式が定数値falseではない場合です。
次の少なくとも 1 つが当てはまる場合、 while ステートメントのエンドポイントに到達できます。
-
whileステートメントには、breakステートメントを終了する到達可能なwhileステートメントが含まれています。 -
whileステートメントに到達可能であり、ブール式には定数値trueがありません。
13.9.3 do ステートメント
do ステートメントは、埋め込みステートメントを 1 回以上条件付きで実行します。
do_statement
: 'do' embedded_statement 'while' '(' boolean_expression ')' ';'
;
do ステートメントは次のように実行されます。
- コントロールは埋め込みステートメントに転送されます。
- コントロールが (
continueステートメントの実行から) 埋め込みステートメントのエンドポイントに到達すると、 boolean_expression (§12.26) が評価されます。 ブール式がtrueを生成する場合は、doステートメントの先頭に制御が転送されます。 それ以外の場合、制御はdoステートメントのエンドポイントに転送されます。
do ステートメントの埋め込みステートメント内で、 breakステートメント (§13.10.2) を使用して、do ステートメントのエンドポイントに制御を転送できます (したがって、埋め込みステートメントの反復処理を終了します)、continueステートメント (§13.10.3) を使用して、埋め込みステートメントのエンドポイントに制御を転送できます (したがって、doステートメントのもう 1 つの反復処理を実行します)。
do ステートメントに到達可能な場合、do ステートメントの埋め込みステートメントに到達できます。
次の少なくとも 1 つが当てはまる場合、 do ステートメントのエンドポイントに到達できます。
-
doステートメントには、breakステートメントを終了する到達可能なdoステートメントが含まれています。 - 埋め込みステートメントの終点に到達可能であり、ブール式には定数値
trueがありません。
13.9.4 for ステートメント
for ステートメントは初期化式のシーケンスを評価し、条件が true の場合は、埋め込みステートメントを繰り返し実行し、反復式のシーケンスを評価します。
for_statement
: 'for' '(' for_initializer? ';' for_condition? ';' for_iterator? ')'
embedded_statement
;
for_initializer
: local_variable_declaration
| statement_expression_list
;
for_condition
: boolean_expression
;
for_iterator
: statement_expression_list
;
statement_expression_list
: statement_expression (',' statement_expression)*
;
for_initializerが存在する場合は、コンマで区切られたlocal_variable_declaration (§13.6.2) またはstatement_expressionのリスト (§13.7) で構成されます。 for_initializerによって宣言されるローカル変数のスコープは、for_initializer、for_condition、for_iterator、およびembedded_statementです。
for_conditionが存在する場合は、boolean_expression (§12.26) にする必要があります。
for_iterator (存在する場合) は、コンマで区切られたstatement_expression (§13.7) のリストで構成されます。
for ステートメントは次のように実行されます。
- for_initializerが存在する場合、変数初期化子またはステートメント式は、書き込まれた順序で実行されます。 この手順は 1 回だけ実行されます。
- for_conditionが存在する場合は評価されます。
-
for_conditionが存在しない場合、または評価によって
trueが生成された場合、制御は埋め込みステートメントに転送されます。 コントロールが (continueステートメントの実行から) 埋め込みステートメントのエンドポイントに到達すると、 for_iteratorの式 (存在する場合) が順番に評価され、上の手順の for_condition の評価から始めて、別の反復が実行されます。 -
for_conditionが存在し、評価によって
falseが生成された場合、制御はforステートメントのエンドポイントに転送されます。
for ステートメントに埋め込まれたステートメント内では、break ステートメント (§13.10.2) を使用して、for ステートメントの終点に制御を転送することで埋め込まれたステートメントの繰り返しを終了できます。また、continue ステートメント (§13.10.3) を利用して、埋め込まれたステートメントの終点に制御を移して、for_iterator を実行し、for から始めて ステートメントの別の繰り返しを行うことができます。
次のいずれかに該当する場合、 for ステートメントの埋め込みステートメントに到達できます。
-
for文には到達可能で、for_conditionは存在しません。 -
forステートメントに到達でき、for_conditionが存在し、falseの定数値を持たない。
次の少なくとも 1 つが当てはまる場合、 for ステートメントのエンドポイントに到達できます。
-
forステートメントには、breakステートメントを終了する到達可能なforステートメントが含まれています。 -
forステートメントに到達でき、for_conditionが存在し、trueの定数値を持たない。
13.9.5 foreach ステートメント
13.9.5.1 全般
foreach ステートメントはコレクションの要素を列挙し、コレクションの各要素に対して埋め込みステートメントを実行します。
foreach_statement
: 'await'? 'foreach' '(' ref_kind? local_variable_type identifier
'in' expression ')' embedded_statement
;
foreach ステートメントの local_variable_type と identifier は、ステートメントの iteration 変数 を宣言します。
var識別子がlocal_variable_typeとして指定され、varという名前の型がスコープ内に存在しない場合、反復変数は単純に型指定された反復変数と呼ばれ、その型は次に示すように、foreach ステートメントの要素型と見なされます。
awaitとref_kindの両方がforeach statementに存在するコンパイル時エラーです。
foreach_statementにrefとreadonlyの両方を含むか、どちらも含まない場合、反復変数は読み取り専用として扱われる変数を表します。 それ以外の場合、foreach_statementにrefのないreadonlyが含まれている場合、反復変数は書き込み可能な変数を表します。
反復変数は、埋め込みステートメントに拡張されるスコープを持つローカル変数に対応します。
foreach ステートメントの実行中、イテレーション変数は、イテレーションが現在実行されているコレクション要素を表します。 反復変数が読み取り専用変数を示す場合、埋め込みステートメントが (代入演算子または ++ 演算子と -- 演算子を使用して) 変更しようとした場合、または参照パラメーターまたは出力パラメーターとして渡そうとすると、コンパイル時エラーが発生します。
foreach ステートメントのコンパイル時の処理では、まず、式のコレクション型 (C)、列挙子の型 (E)、および反復型 (T、ref T、またはref readonly T) が決定されます。
決定は、同期バージョンと非同期バージョンに似ています。 異なるメソッドと戻り値の型を持つ異なるインターフェイスは、同期バージョンと非同期バージョンを区別します。 一般的なプロセスは次のように進みます。 '«' および '»' 内の名前は、同期反復子と非同期反復子の実際の名前のプレースホルダーです。 «GetEnumerator»、«MoveNext»、«IEnumerable»<T>、«IEnumerator»<T>、およびその他の区別は、同期ステートメントのforeach、非同期ステートメントの場合は foreach で詳しく説明されています。
-
Xの型に適切な «GetEnumerator» メソッドがあるかどうかを判断します。- 識別子 «GetEnumerator» を持ち、型引数を持たない型
Xに対してメンバー参照を実行します。 メンバー参照で一致が生成されない場合、またはあいまいさが生成される場合、またはメソッド グループではない一致が生成される場合は、手順 2 で説明されているように列挙可能なインターフェイスを確認します。 メンバー参照でメソッド グループ以外の何かを生成する場合、または一致するものがない場合は、警告を発行することをお勧めします。 - 結果のメソッド グループと空の引数リストを使用して、オーバーロードの解決を実行します。 オーバーロードの解決によって該当するメソッドが存在しない場合、あいまいになる場合、または単一の最適なメソッドになるが、そのメソッドが静的であるかパブリックでない場合は、次に説明するように列挙可能なインターフェイスを確認します。 オーバーロードの解決で明確なパブリック インスタンス メソッド以外の何かを生成する場合、または該当するメソッドが生成されない場合は、警告を発行することをお勧めします。
- «GetEnumerator» メソッドの戻り値の型
Eがクラス、構造体、またはインターフェイス型でない場合は、エラーが発生し、それ以上の手順は実行されません。 - 識別子
Eを持ち、型引数を持たないCurrentに対してメンバー参照を実行します。 メンバー参照で一致が生成されない場合、結果はエラーになります。または、読み取りを許可し、エラーを生成し、それ以上の手順を実行しないパブリック インスタンス プロパティ以外の結果になります。 - 識別子 «MoveNext» を使用し、型引数を指定しない
Eに対してメンバー検索を実行します。 メンバー参照で一致が生成されない場合、結果はエラーになります。または、結果がメソッド グループを除くものになる場合は、エラーが生成され、それ以上の手順は実行されません。 - 空の引数リストを使用して、メソッド グループに対してオーバーロード解決を実行します。 オーバーロードの解決の結果が次の結果の場合: 該当するメソッドはありません。あいまいさ。または単一の最良のメソッドですが、そのメソッドが静的であるか、パブリックでないか、またはその戻り値の型が許可された戻り値の型ではありません。エラーを生成し、それ以上の手順を実行しません。
- コレクション型が
Xされ、列挙子の型がEされ、反復処理の型がCurrentプロパティの型です。
- 識別子 «GetEnumerator» を持ち、型引数を持たない型
- それ以外の場合は、列挙可能なインターフェイスを確認します。
-
Tᵢから «IEnumerable»XTi< への暗黙的な変換があるすべての型>場合、TがTではなく、他のすべてのdynamicに対して «IEnumerable»TᵢT< から «IEnumerable»>Ti< への暗黙的な変換が行われるような一意の型>があります コレクション型はインターフェイス «IEnumerable»<T>、列挙子の型はインターフェイス «IEnumerator»<T> イテレーションの種類がT。 - それ以外の場合は、このような型が複数存在する場合
T、エラーが発生し、それ以上の手順は実行されません。
-
注: 式 に値
nullがある場合、実行時にSystem.NullReferenceExceptionがスローされます。 注釈
実装では、特定のforeach_statementを異なる方法で実装できます。たとえば、パフォーマンス上の理由から、動作が §13.9.5.2 および §13.9.5.3 に記載されている拡張と一致している限り。
13.9.5.2 同期 foreach
同期foreachには、await キーワードの前に foreach キーワードは含まれません。
コレクション型、列挙型、および反復型の決定は、§13.9.5.1 で説明されているように続行されます。次の場合:
- «GetEnumerator» は
GetEnumeratorメソッドです。 - «MoveNext» は、
MoveNext戻り値の型を持つboolメソッドです。 - «IEnumerable»<T> は
System.Collections.Generic.IEnumerable<T>インターフェイスです。 - «IEnumerator»<T> は
System.Collections.Generic.IEnumerator<T>インターフェイスです。
さらに、 §13.9.5.1 の手順に次の変更が加えられます。
§13.9.5.1 で説明されているプロセスの前に、次の手順を実行します。
-
Xの型が配列型の場合、XからIEnumerable<T>インターフェイスへの暗黙的な参照変換があります。ここで、Tは配列Xの要素型です (§17.2.3)。 -
expression の型
Xがdynamic場合、expressionからIEnumerableインターフェイス (§10.2.10) への暗黙的な変換があります。 コレクション型はIEnumerableインターフェイスであり、列挙子の型はIEnumeratorインターフェイスです。var識別子がlocal_variable_typeとして指定されている場合、反復型はdynamicされ、それ以外の場合はobject。
§13.9.5.1 のプロセスが単一のコレクション型、列挙子型、および反復型を生成せずに完了した場合は、次の手順が実行されます。
-
XからSystem.Collections.IEnumerableインターフェイスへの暗黙的な変換がある場合、コレクション型はこのインターフェイス、列挙子の型はインターフェイスSystem.Collections.IEnumerator、反復処理の型はobject。 - それ以外の場合はエラーが生成され、それ以上の手順は実行されません。
フォームの foreach ステートメント
foreach (V v in x) «embedded_statement»
は次のようになります。
{
E e = ((C)(x)).GetEnumerator();
try
{
while (e.MoveNext())
{
V v = (V)(T)e.Current;
«embedded_statement»
}
}
finally
{
... // Dispose e
}
}
e変数は、式x、埋め込みステートメント、またはプログラムのその他のソース コードに対して表示またはアクセスできません。
v変数は、埋め込みステートメントでは読み取り専用です。
(反復型) からT (V ステートメントのlocal_variable_type) への明示的な変換 (foreach) がない場合は、エラーが生成され、それ以上の手順は実行されません。
反復変数が参照変数 (§9.7) の場合、フォームの foreach ステートメント
foreach (ref V v in x) «embedded_statement»
は次のようになります。
{
E e = ((C)(x)).GetEnumerator();
try
{
while (e.MoveNext())
{
ref V v = ref e.Current;
«embedded_statement»
}
}
finally
{
... // Dispose e
}
}
変数 e は、式 x 、埋め込みステートメント、またはプログラムのその他のソース コードに対して表示またはアクセスできません。
v参照変数は埋め込みステートメントでは読み取り/書き込みですが、vは再割り当てできません (§12.23.3)。
(反復型) からT (V ステートメントのlocal_variable_type) への ID 変換 (foreach) がない場合は、エラーが生成され、それ以上の手順は実行されません。
形式 foreach の foreach (ref readonly V v in x) «embedded_statement» ステートメントも同様の同等の形式を持ちますが、埋め込みステートメントでは参照変数 v が ref readonly であるため、ref 再代入または再代入することはできません。
while ループ内でのvの配置は、embedded_statementで発生する匿名関数によってキャプチャされる方法 (§12.21.6.2) にとって重要です。
例:
int[] values = { 7, 9, 13 }; Action f = null; foreach (var value in values) { if (f == null) { f = () => Console.WriteLine("First value: " + value); } } f();展開された形式の
vがwhileループの外部で宣言された場合、すべてのイテレーション間で共有され、forループの後の値は最終的な値13になり、fの呼び出しは出力されます。 代わりに、各イテレーションには独自の変数vがあるため、最初のイテレーションでfによってキャプチャされたものは、出力される値7を保持し続けます。 (以前のバージョンの C# では、vはwhileループの外部で宣言されていたことに注意してください)。終了サンプル
finally ブロックの本体は、次の手順に従って構築されます。
EからSystem.IDisposableインターフェイスへの暗黙的な変換がある場合は、Eが、null 許容値型以外の場合、finally句は、次のセマンティックに相当するものに展開されます。finally { ((System.IDisposable)e).Dispose(); }それ以外の場合、
finally句は次のセマンティックに相当するものに拡張されます。finally { System.IDisposable d = e as System.IDisposable; if (d != null) { d.Dispose(); } }ただし、
Eが値型または値型にインスタンス化された型パラメーターである場合、eからSystem.IDisposableへの変換ではボックス化は行われません。
それ以外の場合、
Eがシール型の場合、finally句は空のブロックに展開されます。finally {}それ以外の場合、
finally句は次のように展開されます。finally { System.IDisposable d = e as System.IDisposable; if (d != null) { d.Dispose(); } }
ローカル変数 d は、どのユーザー コードにも表示されず、アクセスできません。 特に、スコープに finally ブロックが含まれる他の変数と競合することはありません。
foreachが配列の要素を走査する順序は次のとおりです。1 次元配列の場合、インデックス 0 から始まり、インデックス Length – 1で終わるインデックスの順序を増やして要素が走査されます。 多次元配列の場合、要素は右端の次元から始めて、その次の左側の次元へと順にインデックスが増加するように走査されます。
例: 次の例では、要素の順序で 2 次元配列の各値を出力します。
class Test { static void Main() { double[,] values = { {1.2, 2.3, 3.4, 4.5}, {5.6, 6.7, 7.8, 8.9} }; foreach (double elementValue in values) { Console.Write($"{elementValue} "); } Console.WriteLine(); } }生成される出力は次のとおりです。
1.2 2.3 3.4 4.5 5.6 6.7 7.8 8.9終了サンプル
例: 次の例では
int[] numbers = { 1, 3, 5, 7, 9 }; foreach (var n in numbers) { Console.WriteLine(n); }
nの型は、int、numbersの反復型と推定されます。終了サンプル
13.9.5.3 await foreach
非同期 foreach では、 await foreach 構文が使用されます。
コレクション型、列挙型、および反復型の決定は、§13.9.5.1 で説明されているように続行されます。次の場合:
- «GetEnumerator» は、待機可能な戻り値の型 (
GetEnumeratorAsync) を持つメソッドです。 - «MoveNext» は、待機可能な戻り値の型 (
MoveNextAsync) を持つメソッドで、await_expressionはboolとして分類されます (§12.9.9.3)。 - «IEnumerable»<T> は
System.Collections.Generic.IAsyncEnumerable<T>インターフェイスです。 - «IEnumerator»<T> は
System.Collections.Generic.IAsyncEnumerator<T>インターフェイスです。
ステートメントのが参照変数 (await foreach) になるというエラーです。
フォームの await foreach ステートメント
await foreach (T item in enumerable) «embedded_statement»
は意味的に次と同等です。
var enumerator = enumerable.GetAsyncEnumerator();
try
{
while (await enumerator.MoveNextAsync())
{
T item = enumerator.Current;
«embedded_statement»
}
}
finally
{
// dispose of enumerator as described later in this clause.
}
式 enumerable がメソッド呼び出し式を表し、パラメーターの 1 つが EnumeratorCancellationAttribute (§23.5.8) でマークされている場合、 CancellationToken は GetAsyncEnumerator メソッドに渡されます。 他のライブラリ メソッドでは、CancellationTokenにGetAsyncEnumeratorを渡す必要がある場合があります。 これらのメソッドが式 enumerableの一部である場合、トークンは、 CreateLinkedTokenSource とその Token プロパティのように 1 つのトークンに結合されます。
finally ブロックの本体は、次の手順に従って構築されます。
戻り値
E型が待機可能なアクセス可能なDisposeAsync()メソッドがある場合 (§12.9.9.2)、finally句はセマンティックに相当する次のように拡張されます。finally { await e.DisposeAsync(); }それ以外の場合、
EからSystem.IAsyncDisposableインターフェイスへの暗黙的な変換があり、Eが null 非許容値型の場合、finally句はセマンティックに相当する型に展開されます。finally { await ((System.IAsyncDisposable)e).DisposeAsync(); }ただし、
Eが値型または値型にインスタンス化された型パラメーターである場合、eからSystem.IAsyncDisposableへの変換ではボックス化は行われません。それ以外の場合、
Eがref struct型で、アクセス可能なDispose()メソッドを持つ場合、finally句は次のセマンティックに対応するように拡張されます。finally { e.Dispose(); }それ以外の場合、
Eがシール型の場合、finally句は空のブロックに展開されます。finally {}それ以外の場合、
finally句は次のように展開されます。finally { System.IAsyncDisposable d = e as System.IAsyncDisposable; if (d != null) { await d.DisposeAsync(); } }
ローカル変数 d は、どのユーザー コードにも表示されず、アクセスできません。 特に、スコープに finally ブロックが含まれる他の変数と競合することはありません。
注: 非同期破棄メカニズムを使用できない場合、
await foreachを同期的に破棄するためにeは必要ありません。 注釈
13.10 ジャンプ ステートメント
13.10.1 全般
ジャンプ ステートメントは無条件に制御を転送します。
jump_statement
: break_statement
| continue_statement
| goto_statement
| return_statement
| throw_statement
;
ジャンプ ステートメントが制御を転送する場所は、jump ステートメントの target と呼ばれます。
jump ステートメントがブロック内で発生し、そのターゲットがそのブロックの外側にある場合、そのステートメントはブロックを抜けると言われます。 ジャンプ ステートメントはブロックから制御を転送できますが、制御をブロックに転送することはできません。
ジャンプ ステートメントの実行は、介入する try ステートメントが存在することによって複雑になります。 このような try ステートメントがない場合、ジャンプ ステートメントは、ジャンプ ステートメントからターゲットに無条件に制御を転送します。 このような介在する try ステートメントが存在する場合、実行はより複雑になります。 ジャンプ ステートメントが関連付けられた try ブロックを持つ 1 つ以上のfinally ブロックを終了すると、制御は最初に最も内側のfinally ステートメントのtry ブロックに転送されます。 コントロールが finally ブロックの終点に達した場合、次に含まれる finally 文の try ブロックにコントロールが転送されます。 このプロセスは、介在するすべてのfinallyステートメントのtry ブロックが実行されるまで繰り返されます。
例: 次のコード内
class Test { static void Main() { while (true) { try { try { Console.WriteLine("Before break"); break; } finally { Console.WriteLine("Innermost finally block"); } } finally { Console.WriteLine("Outermost finally block"); } } Console.WriteLine("After break"); } }2 つの
finallyステートメントに関連付けられているtryブロックは、制御がジャンプ ステートメントのターゲットに転送される前に実行されます。 生成される出力は次のとおりです。Before break Innermost finally block Outermost finally block After break終了サンプル
13.10.2 break ステートメント
break ステートメントは、最内の switch、while、do、for、または foreach ステートメントから抜けます。
break_statement
: 'break' ';'
;
break ステートメントのターゲットは、囲んでいる最も近いswitch、while、do、for、またはforeachステートメントの終点です。
breakステートメントがswitch、while、do、for、またはforeachステートメントで囲まれていない場合は、コンパイル時エラーが発生します。
複数の switch、 while、 do、 for、または foreach ステートメントが相互に入れ子になっている場合、 break ステートメントは最も内側のステートメントにのみ適用されます。 複数の入れ子レベル間で制御を転送するには、 goto ステートメント (§13.10.4) を使用する必要があります。
break ステートメントは、finally ブロック (§13.11) を終了できません。
break ブロック内でfinallyステートメントが発生した場合、break ステートメントのターゲットは同じfinally ブロック内に存在する必要があります。それ以外の場合は、コンパイル時エラーが発生します。
break ステートメントは次のように実行されます。
-
breakステートメントが関連付けられたtryブロックを持つ 1 つ以上のfinallyブロックを終了すると、制御は最初に最も内側のfinallyステートメントのtryブロックに転送されます。 コントロールがfinallyブロックの終点に達した場合、次に含まれるfinally文のtryブロックにコントロールが転送されます。 このプロセスは、介在するすべてのfinallyステートメントのtryブロックが実行されるまで繰り返されます。 - 制御は、
breakステートメントのターゲットに転送されます。
break ステートメントは無条件に制御を他の場所に転送するため、break ステートメントのエンドポイントに到達できません。
13.10.3 continue ステートメント
continue ステートメントは、最も内側の while、do、for、または foreach の反復を開始します。
continue_statement
: 'continue' ';'
;
continue ステートメントのターゲットは、最も内側の while、do、for、または foreach ステートメントの埋め込みステートメントのエンド ポイントです。
continueステートメントがwhile、do、for、またはforeachステートメントで囲まれていない場合は、コンパイル時エラーが発生します。
複数の while、 do、 for、または foreach ステートメントが相互に入れ子になっている場合、 continue ステートメントは最も内側のステートメントにのみ適用されます。 複数の入れ子レベル間で制御を転送するには、 goto ステートメント (§13.10.4) を使用する必要があります。
continue ステートメントは、finally ブロック (§13.11) を終了できません。
continue ブロック内でfinallyステートメントが発生した場合、continue ステートメントのターゲットは同じfinally ブロック内に存在する必要があります。それ以外の場合は、コンパイル時エラーが発生します。
continue ステートメントは次のように実行されます。
-
continueステートメントが関連付けられたtryブロックを持つ 1 つ以上のfinallyブロックを終了すると、制御は最初に最も内側のfinallyステートメントのtryブロックに転送されます。 コントロールがfinallyブロックの終点に達した場合、次に含まれるfinally文のtryブロックにコントロールが転送されます。 このプロセスは、介在するすべてのfinallyステートメントのtryブロックが実行されるまで繰り返されます。 - 制御は、
continueステートメントのターゲットに転送されます。
continue ステートメントは無条件に制御を他の場所に転送するため、continue ステートメントのエンドポイントに到達できません。
13.10.4 goto ステートメント
goto ステートメントは、ラベルでマークされたステートメントに制御を転送します。
goto_statement
: 'goto' identifier ';'
| 'goto' 'case' constant_expression ';'
| 'goto' 'default' ';'
;
goto
identifier ステートメントのターゲットは、指定されたラベルを持つラベル付きステートメントです。 指定された名前のラベルが現在の関数メンバーに存在しない場合、または goto ステートメントがラベルのスコープ内にない場合は、コンパイル時エラーが発生します。
注: この規則では、入れ子になったスコープから制御を
goto転送するステートメントを使用できますが、入れ子になったスコープに制御を転送することはできません。 この例では、class Test { static void Main(string[] args) { string[,] table = { {"Red", "Blue", "Green"}, {"Monday", "Wednesday", "Friday"} }; foreach (string str in args) { int row, colm; for (row = 0; row <= 1; ++row) { for (colm = 0; colm <= 2; ++colm) { if (str == table[row,colm]) { goto done; } } } Console.WriteLine($"{str} not found"); continue; done: Console.WriteLine($"Found {str} at [{row}][{colm}]"); } } }
gotoステートメントは、入れ子になったスコープから制御を転送するために使用されます。注釈
goto case ステートメントのターゲットは、指定された定数値の定数パターンを持っているもののガードはない switch ラベルを含む、すぐ外側の ステートメント (case) のステートメント一覧です。
goto caseステートメントがswitchステートメントで囲まれていない場合、最も近い外側のswitchステートメントにそのようなcaseが含まれていない場合、またはconstant_expressionが最も近い外側のステートメントの制御型に暗黙的に変換できない場合 (switch) 場合、コンパイル時エラーが発生します。
goto default ステートメントのターゲットは、switch ラベルを含む、すぐ外側の ステートメント (default) のステートメント一覧です。
goto defaultステートメントがswitchステートメントで囲まれていない場合、または最も近い外側のswitchステートメントにdefaultラベルが含まれていない場合は、コンパイル時エラーが発生します。
goto ステートメントは、finally ブロック (§13.11) を終了できません。
goto ステートメントがfinally ブロック内で発生した場合、goto ステートメントのターゲットは同じfinally ブロック内にあるか、それ以外の場合はコンパイル時エラーが発生します。
goto ステートメントは次のように実行されます。
-
gotoステートメントが関連付けられたtryブロックを持つ 1 つ以上のfinallyブロックを終了すると、制御は最初に最も内側のfinallyステートメントのtryブロックに転送されます。 コントロールがfinallyブロックの終点に達した場合、次に含まれるfinally文のtryブロックにコントロールが転送されます。 このプロセスは、介在するすべてのfinallyステートメントのtryブロックが実行されるまで繰り返されます。 - 制御は、
gotoステートメントのターゲットに転送されます。
goto ステートメントは無条件に制御を他の場所に転送するため、goto ステートメントのエンドポイントに到達できません。
13.10.5 return ステートメント
return ステートメントは、return ステートメントが出現する関数メンバーの現在の呼び出し元に制御を返し、必要に応じて値またはvariable_reference (§9.5) を返します。
return_statement
: 'return' ';'
| 'return' expression ';'
| 'return' 'ref' variable_reference ';'
;
expression を含まない return_statement は return-no-value と呼ばれ、refexpression を含むものは return-by-ref と呼ばれ、expression のみを含むものは return-by-value と呼ばれます。
値による戻り値または returns by-ref (§15.6.1) として宣言されたメソッドからの戻り値なし値を使用するのはコンパイル時エラーです。
return-by-ref を使用することは、returns-no-value または returns-by-value として宣言されたメソッドにおいてコンパイル時エラーです。
returns-no-value または returns-by-ref として宣言されたメソッドから値による戻り値を使用するのはコンパイル時エラーです。
expressionがvariable_referenceでない、または参照する変数のref-safeコンテキストが呼び出し元コンテキスト(§9.7.2)でない場合、参照による戻り値の使用はコンパイル時エラーとなります。
method_modifierasyncで宣言されたメソッドから ref による戻り値を使用するのはコンパイル時エラーです。
関数メンバーは、returns-by-value (§15.6.11) メソッド、プロパティまたはインデクサーのアクセサーを取得する returns-by-value、またはユーザー定義演算子を持つメソッドである場合、値を計算すると言われます。 値を返さない関数メンバーは、値を計算せず、有効な戻り値の型 void、プロパティとインデクサーのアクセサーの設定、イベント、インスタンス コンストラクター、静的コンストラクター、ファイナライザーのアクセサーの追加と削除を行うメソッドです。 ref で返される関数メンバーは、値を計算しません。
return-by-value の場合、暗黙的な変換 (§10.2) は、expression 型から、含まれている関数メンバーの有効な戻り値の型 (§15.6.11) にまで存在します。 参照による戻り値の場合、アイデンティティ変換 (§10.2.2) は、expression を含む関数メンバーの有効な返り値型の間に存在する必要があります。
return ステートメントは、匿名関数式 (§12.21) の本体でも使用でき、それらの関数に対して存在する変換の決定に参加できます (§10.7.1)。
return ステートメントが finally ブロックに表示されるコンパイル時エラーです (§13.11)。
return ステートメントは次のように実行されます。
- 値を返す場合、expression は評価され、その値は包含する関数の事実上の戻り値の型に暗黙の変換によって変換されます。 変換の結果は、関数によって生成された結果値になります。 ref による戻り値の場合、 式 が評価され、結果は変数として分類されます。 囲むメソッドの return-by-ref に
readonlyが含まれている場合、結果の変数は読み取り専用になります。 -
returnステートメントが 1 つ以上のtryまたは関連するcatchブロックを持つfinallyブロックで囲まれている場合、制御は最初に最も内側のfinallyステートメントのtryブロックに転送されます。 コントロールがfinallyブロックの終点に達した場合、次に含まれるfinally文のtryブロックにコントロールが転送されます。 このプロセスは、すべての外側のfinallyステートメントのtryブロックが実行されるまで繰り返されます。 - 包含関数が非同期関数でない場合は、制御が結果値 (存在する場合) と共に、包含関数の呼び出し元に返されます。
- 包含関数が非同期関数の場合、制御は現在の呼び出し元に返され、結果値がある場合は、(§15.14.3) で説明されているように戻りタスクに記録されます。
return ステートメントは無条件に制御を他の場所に転送するため、return ステートメントのエンドポイントに到達できません。
13.10.6 throw ステートメント
throw ステートメントにより例外がスローされます。
throw_statement
: 'throw' expression? ';'
;
式を含む throw ステートメントは、式を評価することによって生成された例外をスローします。 式は暗黙的に System.Exceptionに変換でき、式を評価した結果はスローされる前に System.Exception に変換されます。 変換の結果が null場合は、代わりに System.NullReferenceException がスローされます。
式のない throw ステートメントは、 catch ブロックでのみ使用できます。その場合、そのステートメントは、その catch ブロックによって現在処理されている例外を再スローします。
throw ステートメントは無条件に制御を他の場所に転送するため、throw ステートメントのエンドポイントに到達できません。
例外がスローされると、例外を処理できる最初の catch 句がある外側の try ステートメントに制御が転送されます。 例外がスローされた時点から適切な例外ハンドラーに制御を転送する時点までのプロセスは、 例外伝達と呼ばれます。 例外の伝達は、例外に一致する catch 句が見つかるまで、次の手順を繰り返し評価することで構成されます。 この説明では、スロー ポイントは、例外がスローされる最初の位置です。 この動作は (§22.4) で指定されています。
現在の関数メンバーで、スロー ポイントを囲む各
tryステートメントが調べられます。 ステートメントSごとに、最も内側のtryステートメントから始まり、最も外側のtryステートメントで終わると、次の手順が評価されます。-
tryのSブロックがスロー ポイントを囲み、Sに 1 つ以上のcatch句がある場合、catch句は、例外に適したハンドラーを見つけるために外観順に調べされます。 例外の型catch(または、実行時に例外の型Tを示す型パラメーター) を指定する最初のT句で、実行時の型EがTから派生するものは、一致とみなされます。 句に例外フィルターが含まれている場合、例外オブジェクトが例外変数に割り当てられ、例外フィルターが評価されます。catch句に例外フィルターが含まれている場合、そのcatch句は、例外フィルターがtrueと評価された場合、一致と見なされます。 一般的なcatch(§13.11) 句は、すべての例外の種類に一致すると見なされます。 一致するcatch句がある場合は、そのcatch句のブロックに制御を転送することで例外伝達が完了します。 - それ以外の場合、
tryブロックまたはcatchのSブロックがスロー ポイントを囲み、Sにfinallyブロックがある場合は、finallyブロックに制御が転送されます。finallyブロックによって別の例外がスローされた場合、現在の例外の処理は終了されます。 それ以外の場合、制御がfinallyブロックの終点に達すると、現在の例外の処理が続行されます。
-
現在の関数呼び出しで例外ハンドラーが見つからない場合、関数の呼び出しは終了し、次のいずれかが発生します。
例外処理が現在のスレッド内のすべての関数メンバー呼び出しを終了し、スレッドに例外のハンドラーがないことを示す場合、スレッド自体は終了します。 このような終了の影響は、実装によって定義されます。
13.11 try ステートメント。
try ステートメントは、ブロックの実行中に発生する例外をキャッチするためのメカニズムを提供します。 さらに、 try ステートメントは、コントロールが try ステートメントを離れると常に実行されるコード ブロックを指定する機能を提供します。
try_statement
: 'try' block catch_clauses
| 'try' block catch_clauses? finally_clause
;
catch_clauses
: specific_catch_clause+
| specific_catch_clause* general_catch_clause
;
specific_catch_clause
: 'catch' exception_specifier exception_filter? block
| 'catch' exception_filter block
;
exception_specifier
: '(' type identifier? ')'
;
exception_filter
: 'when' '(' boolean_expression ')'
;
general_catch_clause
: 'catch' block
;
finally_clause
: 'finally' block
;
try_statementは、キーワード tryの後に block、0 個以上のcatch_clauses、オプションのfinally_clauseで構成されます。 少なくとも 1 つの catch_clause または finally_clauseがあります。
exception_specifierでは、型またはその有効な基底クラス (type_parameterの場合) は、System.Exceptionまたは派生する型である必要があります。
catch句で class_type と identifier の両方を指定すると、指定された名前と型の例外変数が宣言されます。 例外変数は、 specific_catch_clause の宣言空間に導入されます (§7.3)。
exception_filterおよびcatch ブロックの実行中、例外変数は現在処理されている例外を表します。 明確な代入チェックのために、例外変数はスコープ全体で確実に割り当てられていると見なされます。
catch句に例外変数名が含まれている場合を除き、フィルターおよびcatch ブロック内の例外オブジェクトにアクセスすることはできません。
例外の種類も例外変数名も指定しない catch 句は、一般的な catch 句と呼ばれます。
try ステートメントは、一般的なcatch句を 1 つだけ持つだけで、存在する場合は最後の catch 句になります。
注: 一部のプログラミング言語では、
System.Exceptionから派生したオブジェクトとして表現できない例外がサポートされている場合がありますが、このような例外は C# コードで生成することはできません。 このような例外をキャッチするには、一般的なcatch句を使用できます。 したがって、一般的なcatch句は、前者が他の言語からの例外もキャッチする可能性があるため、System.Exception型を指定する句とは意味的に異なります。 注釈
例外のハンドラーを見つけるために、 catch 句は字句の順序で調べわれます。
catch句で型を指定しても例外フィルターが指定されていない場合、同じcatch ステートメントの後のtry句でコンパイル時エラーが発生し、その型と同じまたは派生した型が指定されます。
注: この制限がないと、到達不能な
catch節を記述できる可能性があります。 注釈
catch ブロック内では、式のないthrow ステートメント (§13.10.6) を使用して、catch ブロックによってキャッチされた例外を再スローできます。 例外変数に代入すると、再スローされる例外は変更されません。
例: 次のコード内
class Test { static void F() { try { G(); } catch (Exception e) { Console.WriteLine("Exception in F: " + e.Message); e = new Exception("F"); throw; // re-throw } } static void G() => throw new Exception("G"); static void Main() { try { F(); } catch (Exception e) { Console.WriteLine("Exception in Main: " + e.Message); } } }メソッド
F例外をキャッチし、コンソールに診断情報を書き込み、例外変数を変更して、例外を再スローします。 再スローされる例外は元の例外であるため、生成される出力は次のようになります。Exception in F: G Exception in Main: G最初の
catchブロックが現在の例外を再スローせずにeをスローしていた場合、生成される出力は次のようになります。Exception in F: G Exception in Main: F終了サンプル
break ブロックから制御を転送するcontinue、goto、またはfinallyステートメントのコンパイル時エラーです。
break ブロックでcontinue、goto、またはfinallyステートメントが発生した場合、ステートメントのターゲットは同じfinally ブロック内にあるか、コンパイル時エラーが発生します。
return ブロックでfinally ステートメントが発生するのはコンパイル時エラーです。
実行が try ステートメントに達すると、制御は try ブロックに転送されます。 例外が伝達されずに制御が try ブロックの終点に達した場合、制御は finally ブロックに転送されます (存在する場合)。
finally ブロックが存在しない場合は、try ステートメントのエンドポイントに制御が転送されます。
例外が伝播された場合、catch 句は、その例外に対する最初の一致を求めて字句順に調べられます。 一致する catch 句の検索は、§13.10.6 で説明されているように、すべての囲みブロックを使用して続行されます。
catch句は、例外の種類が任意の例外指定子と一致し、かつ任意のexception_filterが true の場合に一致します。
catch 句に例外指定子がない場合、どの例外タイプとも一致します。 例外の種類は、exception_specifierが例外の種類または例外の種類の基本型を指定するときに、exception_specifierと一致します。 句に例外フィルターが含まれている場合、例外オブジェクトが例外変数に割り当てられ、例外フィルターが評価されます。
exception_filterのboolean_expressionの評価で例外がスローされると、その例外がキャッチされ、例外フィルターはfalseに評価されます。
例外が伝達され、一致する catch 句が見つかった場合、制御は最初に一致する catch ブロックに転送されます。 例外が伝達されずに制御が catch ブロックの終点に達した場合、制御は finally ブロックに転送されます (存在する場合)。
finally ブロックが存在しない場合は、try ステートメントのエンドポイントに制御が転送されます。 例外が catch ブロックから伝達された場合は、 finally ブロックに制御が転送されます (存在する場合)。 例外は、次の外側の try ステートメントに伝播されます。
例外が伝達され、一致する catch 句が見つからない場合は、 finally ブロックに制御が転送されます (存在する場合)。 例外は、次の外側の try ステートメントに伝播されます。
finally ブロックのステートメントは、制御が try ステートメントを離れるときに常に実行されます。 これは、 break、 continue、 goto、または return ステートメントの実行の結果として、または try ステートメントから例外を伝達した結果として、コントロール転送が通常の実行の結果として発生するかどうかに当てはまります。 例外が伝達されずに制御が finally ブロックの終点に達すると、制御は try ステートメントのエンドポイントに転送されます。
finally ブロックの実行中に例外がスローされ、同じfinally ブロック内でキャッチされない場合、例外は次に囲むtryステートメントに反映されます。 別の例外が伝達中であった場合、その例外は失われます。 例外を伝達するプロセスについては、 throw ステートメント (§13.10.6) の説明で詳しく説明します。
例: 次のコード内
public class Test { static void Main() { try { Method(); } catch (Exception ex) when (ExceptionFilter(ex)) { Console.WriteLine("Catch"); } bool ExceptionFilter(Exception ex) { Console.WriteLine("Filter"); return true; } } static void Method() { try { throw new ArgumentException(); } finally { Console.WriteLine("Finally"); } } }メソッド
Methodは例外を発生させます。 最初のアクションは、外側のcatch句を調べ、例外フィルターを適用することです。 次に、finallyのMethod句が実行されてから、制御が外側の一致するcatch句に転送されます。 結果の出力は次のとおりです。Filter Finally Catch終了サンプル
try ステートメントに到達可能な場合、try ステートメントのtry ブロックに到達できます。
catch ステートメントに到達可能な場合、try ステートメントのtry ブロックに到達できます。
finally ステートメントに到達可能な場合、try ステートメントのtry ブロックに到達できます。
次の両方に該当する場合、 try ステートメントのエンドポイントに到達できます。
-
tryブロックの終点に到達可能であるか、少なくとも 1 つのcatchブロックの終点に到達可能です。 -
finallyブロックが存在する場合、finallyブロックの終点に到達できます。
13.12 チェックされたステートメントとチェックされていないステートメント
checkedステートメントとunchecked ステートメントは、整数型の算術演算と変換のオーバーフロー チェック コンテキストを制御するために使用されます。
checked_statement
: 'checked' block
;
unchecked_statement
: 'unchecked' block
;
checked ステートメントを使用すると、block 内のすべての式がチェック コンテキストで評価され、unchecked ステートメントによって、block内のすべての式がチェックされていないコンテキストで評価されます。
checkedステートメントと unchecked ステートメントは、式ではなくブロックで動作することを除き、checkedおよびunchecked演算子 (§12.8.20) と正確に等しくなります。
13.13 lock ステートメント
lock ステートメントは、特定のオブジェクトの相互排他ロックを取得し、ステートメントを実行して、ロックを解放します。
lock_statement
: 'lock' '(' expression ')' embedded_statement
;
式がlockステートメントの形式であり、参照であることが知られている型の値を示す必要があります。 暗黙的なボックス化変換 (§10.2.9) は、 ステートメントのlockに対して実行されることがないため、式がvalue_typeの値を示すコンパイル時エラーになります。
フォームの lock ステートメント
lock (x) …
ここで、 x は reference_typeの式であり、次の式と正確に等しくなります。
bool __lockWasTaken = false;
try
{
System.Threading.Monitor.Enter(x, ref __lockWasTaken);
...
}
finally
{
if (__lockWasTaken)
{
System.Threading.Monitor.Exit(x);
}
}
ただし、x が評価されるのは 1 回だけです。
相互排他ロックが保持されている間、同じ実行スレッドで実行されるコードでもロックを取得して解放できます。 ただし、他のスレッドで実行されているコードは、ロックが解放されるまでロックの取得をブロックされます。
13.14 using ステートメント
13.14.1 全般
using ステートメントは、1 つ以上のリソースを取得し、ステートメントを実行してから、リソースを破棄します。
using_statement
: 'await'? 'using' '(' resource_acquisition ')' embedded_statement
;
resource_acquisition
: non_ref_local_variable_declaration
| expression
;
non_ref_local_variable_declaration
: implicitly_typed_local_variable_declaration
| explicitly_typed_local_variable_declaration
;
リソースの種類は、System.IDisposableインターフェイスまたは System.IAsyncDisposable インターフェイスのいずれかまたは両方を実装するクラスまたは非 ref 構造体です。この構造体には、DisposeまたはDisposeAsyncという名前の単一のパラメーターなしのメソッド、またはDisposeで宣言されたのと同じシグネチャを持つSystem.IDisposableという名前のメソッドを含む ref 構造体が含まれます。 リソースを使用しているコードは、 Dispose または DisposeAsync を呼び出して、リソースが不要であることを示すことができます。
resource_acquisitionの形式がlocal_variable_declaration場合、local_variable_declarationの型はdynamicまたはリソースの種類のいずれかになります。
resource_acquisitionの形式が式の場合、この式はリソースの種類になります。
awaitが存在する場合、リソースの種類はSystem.IAsyncDisposableを実装する必要があります。
ref struct型は、using修飾子を持つawait ステートメントのリソースの種類にすることはできません。
resource_acquisitionで宣言されたローカル変数は読み取り専用であり、初期化子を含める必要があります。 埋め込みステートメントが (代入演算子または ++ 演算子と -- 演算子を使用して) これらのローカル変数を変更しようとした場合、またはそれらのアドレスを参照パラメーターまたは出力パラメーターとして渡そうとすると、コンパイル時エラーが発生します。
usingステートメントは、取得、使用、破棄の 3 つの部分に変換されます。 リソースの使用は、try句を含むfinally ステートメントで暗黙的に囲まれます。 この finally 句は、リソースを破棄します。 取得式が nullと評価された場合、 Dispose (または DisposeAsync) の呼び出しは行われず、例外はスローされません。 リソースの種類が dynamic 場合は、使用と破棄の前に変換が成功するように、取得中に暗黙的な動的変換 (§10.2.10) を使用して IDisposable (または IAsyncDisposable) に動的に変換されます。
フォームの using ステートメント
using (ResourceType resource = «expression» ) «statement»
は、3 つの可能な製剤のいずれかに対応します。 クラスおよび ref 以外の構造体リソースの場合、 ResourceType が null 非許容値型または値型制約 (§15.2.5) を持つ型パラメーターである場合、定式化はセマンティックに同等です。
{
ResourceType resource = «expression»;
try
{
«statement»;
}
finally
{
((IDisposable)resource).Dispose();
}
}
ただし、resource を System.IDisposable にキャストしても、ボックス化は発生しません。
それ以外の場合、 ResourceType が dynamicされると、製剤は
{
ResourceType resource = «expression»;
IDisposable d = resource;
try
{
«statement»;
}
finally
{
if (d != null)
{
d.Dispose();
}
}
}
それ以外の場合、製剤は
{
ResourceType resource = «expression»;
try
{
«statement»;
}
finally
{
IDisposable d = (IDisposable)resource;
if (d != null)
{
d.Dispose();
}
}
}
ref 構造体リソースの場合、意味的に同等の公式化は
{
«ResourceType» resource = «expression»;
try
{
«statement»;
}
finally
{
resource.Dispose();
}
}
任意の定式化では、 resource 変数は埋め込みステートメントでは読み取り専用であり、 d 変数は埋め込みステートメントではアクセスできなくなり、非表示になります。
形式の using ステートメント:
using («expression») «statement»
同じ可能な製剤を有する。
resource_acquisitionがlocal_variable_declarationの形式になると、特定の型の複数のリソースを取得できます。 フォームの using ステートメント
using (ResourceType r1 = e1, r2 = e2, ..., rN = eN) «statement»
入れ子になった using ステートメントのシーケンスと正確に等しくなります。
using (ResourceType r1 = e1)
using (ResourceType r2 = e2)
...
using (ResourceType rN = eN)
«statement»
例: 次の例では、log.txtという名前のファイルを作成し、2 行のテキストをファイルに書き込みます。 次に、読み取り用に同じファイルを開き、含まれているテキスト行をコンソールにコピーします。
class Test { static void Main() { using (TextWriter w = File.CreateText("log.txt")) { w.WriteLine("This is line one"); w.WriteLine("This is line two"); } using (TextReader r = File.OpenText("log.txt")) { string s; while ((s = r.ReadLine()) != null) { Console.WriteLine(s); } } } }
TextWriterクラスとTextReaderクラスはIDisposableインターフェイスを実装するため、この例では、usingステートメントを使用して、書き込み操作または読み取り操作の後に基になるファイルが適切に閉じられるようにすることができます。終了サンプル
ResourceTypeがIAsyncDisposableを実装する参照型である場合。
await using用の他の定式化では、同期Dispose メソッドから非同期DisposeAsync メソッドへの同様の置換が実行されます。 フォームの await using ステートメント
await using (ResourceType resource = «expression» ) «statement»
は、IAsyncDisposableではなくIDisposable、DisposeAsyncではなくDispose、Taskから返されるDisposeAsyncがawaitされた、以下に示す定式化と意味的に同等です。
await using (ResourceType resource = «expression» ) «statement»
は、次と同じ意味です:
{
ResourceType resource = «expression»;
try
{
«statement»;
}
finally
{
IAsyncDisposable d = (IAsyncDisposable)resource;
if (d != null)
{
await d.DisposeAsync();
}
}
}
注: embedded_statement内のすべてのジャンプ ステートメント (§13.10) は、
usingステートメントの拡張形式に準拠している必要があります。 注釈
13.14.2 宣言の使用
using ステートメントの構文バリアントは、 using 宣言です。
using_declaration
: 'await'? 'using' non_ref_local_variable_declaration ';' statement_list?
;
using 宣言は、using ステートメント (§13.14.1) の対応するリソース取得形式と同じセマンティクスを持ち、次のように書き換えることができます。
using «local_variable_type» «local_variable_declarators»
// statements
は、次と同じ意味です:
using («local_variable_type» «local_variable_declarators»)
{
// statements
}
そして
await using «local_variable_type» «local_variable_declarators»
// statements
は、次と同じ意味です:
await using («local_variable_type» «local_variable_declarators»)
{
// statements
}
non_ref_local_variable_declarationで宣言された変数の有効期間は、宣言されるスコープの末尾まで拡張されます。 これらの変数は、宣言されている逆の順序で破棄されます。
static void M()
{
using FileStream f1 = new FileStream(...);
using FileStream f2 = new FileStream(...), f3 = new FileStream(...);
...
// Dispose f3
// Dispose f2
// Dispose f1
}
using 宣言は 、switch_label内に直接表示されるのではなく、 switch_label内のブロック内にある可能性があります。
13.15 yield ステートメント
yield ステートメントは、反復子の列挙子オブジェクト (§15.15.5) または列挙可能なオブジェクト (§15.15.6) に値を生成したり、反復処理の終了を通知したりするために、反復子ブロック (§13.3) で使用されます。
yield_statement
: 'yield' 'return' expression ';'
| 'yield' 'break' ';'
;
yield はコンテキスト キーワード (§6.4.4) であり、 return または break キーワードの直前に使用する場合にのみ特別な意味を持ちます。
次に示すように、 yield ステートメントを使用できる場所にはいくつかの制限があります。
-
yieldステートメント (いずれかの形式) がmethod_body、operator_body、またはaccessor_bodyの外に表示されることはコンパイル時エラーです。 - これは、(いずれかの形式の)
yieldステートメントが匿名関数内に表示されるコンパイル時エラーです。 -
yieldステートメントのfinally句にtryステートメント (いずれかの形式) が含まれる場合は、コンパイル時エラーです。 -
yield returnステートメントがtryを含んでいる場合、 ステートメントがその中にあることはコンパイル時エラーです。
例: 次の例は、
yieldステートメントの有効および無効な使用方法を示しています。delegate IEnumerable<int> D(); IEnumerator<int> GetEnumerator() { try { yield return 1; // Ok yield break; // Ok } finally { yield return 2; // Error, yield in finally yield break; // Error, yield in finally } try { yield return 3; // Error, yield return in try/catch yield break; // Ok } catch { yield return 4; // Error, yield return in try/catch yield break; // Ok } D d = delegate { yield return 5; // Error, yield in an anonymous function }; } int MyMethod() { yield return 1; // Error, wrong return type for an iterator block }終了サンプル
ステートメント内の式の型から反復子の yield 型 (yield return) への暗黙的な変換 (§10.2) が存在する必要があります。
yield return ステートメントは次のように実行されます。
- ステートメントで指定された式が評価され、暗黙的に yield 型に変換され、列挙子オブジェクトの
Currentプロパティに割り当てられます。 - 反復子ブロックの実行が中断されます。
yield returnステートメントが 1 つ以上のtryブロック内にある場合、関連付けられているfinallyブロックは現時点で実行されません。 - 列挙子オブジェクトの
MoveNextメソッドは、trueを呼び出し元に返し、列挙子オブジェクトが次の項目に正常に進んだことを示します。
列挙子オブジェクトの MoveNext メソッドの次の呼び出しは、最後に中断された場所から反復子ブロックの実行を再開します。
yield break ステートメントは次のように実行されます。
-
yield breakステートメントが、関連付けられたtryブロックを持つ 1 つ以上のfinallyブロックで囲まれている場合、制御は最初に最も内側のfinallyステートメントのtryブロックに転送されます。 コントロールがfinallyブロックの終点に達した場合、次に含まれるfinally文のtryブロックにコントロールが転送されます。 このプロセスは、すべての外側のfinallyステートメントのtryブロックが実行されるまで繰り返されます。 - 反復子ブロックの呼び出し元に制御が返されます。 これは、列挙子オブジェクトの
MoveNextメソッドまたはDisposeメソッドです。
yield break ステートメントは無条件に制御を他の場所に転送するため、yield break ステートメントのエンドポイントに到達できません。
ECMA C# draft specification