13 ステートメント
13.1 全般
C# には、さまざまなステートメントが用意されています。
注: これらのステートメントのほとんどは、C および C++ でプログラミングした開発者にはなじみがあります。 end note
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 (§23.2) と fixed_statement (§23.7) は、安全でないコード (§23) でのみ使用できます。
embedded_statement非終端は、他のステートメント内に表示されるステートメントに使用されます。 ステートメントではなくembedded_statementを使用するとこれらのコンテキストでの宣言ステートメントとラベル付きステートメントの使用は除外されます。
例: コード
void F(bool b) { if (b) int i = 44; }
if
ステートメントでは、そのif
ブランチに対してステートメントではなくembedded_statementが必要であるため、コンパイル時エラーが発生します。 このコードが許可されている場合、変数i
が宣言されますが、使用することはできませんでした。 ただし、i
の宣言をブロックに配置することで、この例は有効であることに注意してください。end の例
13.2 エンドポイントと到達可能性
すべてのステートメントには、 エンドポイントがあります。 直感的に言えば、ステートメントの終点は、ステートメントの直後の場所です。 複合ステートメント (埋め込みステートメントを含むステートメント) の実行規則では、コントロールが埋め込みステートメントの終点に達したときに実行されるアクションを指定します。
例: コントロールがブロック内のステートメントの終点に達すると、コントロールはブロック内の次のステートメントに転送されます。 end の例
実行によってステートメントに到達できる可能性がある場合、ステートメントは 到達可能であると言われます。 逆に、ステートメントが実行される可能性がない場合、ステートメントは 到達不能であると言われます。
例: 次のコード内
void F() { Console.WriteLine("reachable"); goto Label; Console.WriteLine("unreachable"); Label: Console.WriteLine("reachable"); }
ステートメントが実行される可能性がないため、Console.WriteLine の 2 回目の呼び出しに到達できません。
end の例
throw_statement、block、またはempty_statement以外のステートメントに到達できない場合、警告が報告されます。 具体的には、ステートメントに到達できないエラーではありません。
注: 特定のステートメントまたはエンドポイントに到達できるかどうかを判断するために、コンパイラは、各ステートメントに定義されている到達可能性ルールに従ってフロー分析を実行します。 フロー分析では、ステートメントの動作を制御する定数式 (§12.23) の値が考慮されますが、非定数式の使用可能な値は考慮されません。 つまり、制御フロー分析のために、特定の型の非定数式は、その型の任意の値を持つと見なされます。
この例では、
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
呼び出しは到達可能と見なされますが、実際には実行されません。end note
関数メンバーまたは匿名関数の ブロック は常に到達可能と見なされます。 ブロック内の各ステートメントの到達可能性ルールを連続して評価することで、特定のステートメントの到達可能性を判断できます。
例: 次のコード内
void F(int x) { Console.WriteLine("start"); if (x < 0) Console.WriteLine("negative"); }
2 番目の
Console.WriteLine
の到達可能性は次のように決定されます。
F
メソッドのブロックに到達可能であるため、最初のConsole.WriteLine
式ステートメントに到達できます (§13.3)。- 最初の
Console.WriteLine
式ステートメントのエンドポイントに到達可能なのは、そのステートメントが到達可能であるためです (§13.7 および §13.3)。- 最初の
Console.WriteLine
式ステートメントのエンドポイントに到達できるため、if
ステートメントに到達できます (§13.7 および §13.3)。if
ステートメントのブール式に定数値false
がないため、2 番目のConsole.WriteLine
式ステートメントに到達できます。end の例
ステートメントのエンドポイントに到達できるコンパイル時エラーは、次の 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
ステートメント (§13.15) を含むブロックは反復子ブロックと呼ばれます。 反復子ブロックは、関数メンバーを反復子として実装するために使用されます (§15.14)。 反復子ブロックには、いくつかの追加の制限が適用されます。
- これは、
return
ステートメントが反復子ブロックに表示されるコンパイル時エラーです (ただし、yield return
ステートメントは許可されています)。 - 反復子ブロックに安全でないコンテキスト (§23.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: ; }
end の例
13.5 ラベル付きステートメント
labeled_statementでは、ステートメントの先頭にラベルを付けます。 ラベル付きステートメントはブロックで許可されますが、埋め込みステートメントとしては使用できません。
labeled_statement
: identifier ':' statement
;
ラベル付きステートメントは、 identifierによって指定された名前を持つラベルを宣言します。 ラベルのスコープは、入れ子になったブロックを含め、ラベルが宣言されているブロック全体です。 同じ名前の 2 つのラベルに重複するスコープがあると、コンパイル時エラーになります。
ラベルは、 goto
ステートメント (§13.10.4) からラベルのスコープ内で参照できます。
注: つまり、
goto
ステートメントは、ブロック内およびブロック外で制御を転送できますが、ブロックに転送することはできません。 end note
ラベルには独自の宣言領域があり、他の識別子に干渉することはありません。
例: 例
int F(int x) { if (x >= 0) { goto x; } x = -x; x: return x; }
は有効であり、パラメーターとラベルの両方として x という名前を使用します。
end の例
ラベル付きステートメントの実行は、ラベルに続くステートメントの実行に正確に対応します。
通常の制御フローによって提供される到達可能性に加えて、ラベル付きステートメントが到達可能なgoto
ステートメントによって参照されている場合、ラベル付きステートメントは到達可能です。ただし、goto
ステートメントがtry
ブロック内にあるか、エンドポイントに到達できないfinally
ブロックを含むtry_statementのcatch
ブロック内にあり、ラベル付きステートメントが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によって導入されます。これは、暗黙的に型指定され、明示的に型指定され、ローカル変数を参照するための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; }
end note
ローカル変数の値は、 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 コンテキストです。 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 番目の代替方法は、初期値が ref
variable_reference の ref 変数を宣言します。T
が null 非許容参照型の場合、その型はref 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
end の例
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
;
explicity_typed_local_variable_declarationでは、指定した type を持つ 1 つ以上のローカル変数が導入されます。
local_variable_initializerが存在する場合、その型は単純な代入 (§12.21.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 は型 type を持ち、 ref 代入 (§12.21.3) の場合と同じ要件を満たす必要があります。
ref_kindがref readonly
の場合、宣言されているidentifierは読み取り専用として扱われる変数への参照です。 それ以外の場合、 ref_kind が ref
されている場合、宣言されている identifier は書き込み可能な変数への参照になります。
method_modifier async
で宣言されたメソッド内、または反復子 (§15.14 内で ref ローカル変数またはref struct
型の変数を宣言するのはコンパイル時エラーです。
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は、定数に名前を付けるidentifierの後に "=
" トークンが続き、定数の値を指定するconstant_expression (§12.23) で構成されます。
ローカル定数宣言の 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 ';'
;
文法上の注意: null_conditional_invocation_expressionと式の両方が適用される場合、local_function_bodyを認識する場合前者が選択されます。 (§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; } } }
end の例
以下で特に指定しない限り、すべての文法要素のセマンティクスは、メソッドではなくローカル関数のコンテキストで読み取 method_declaration (§15.6.1) の場合と同じです。
local_function_declarationのidentifierは、外側のローカル変数宣言スペースを含め、宣言されたブロック スコープ内で一意である必要があります。 この結果の 1 つは、オーバーロードされた local_function_declarationが許可されないことです。
local_function_declarationには、1 つのasync
(§15.15) 修飾子と 1 つのunsafe
(§23.1) 修飾子を含めることができます。 宣言に async
修飾子が含まれている場合、戻り値の型は void
または «TaskType»
型 (§15.15.1) になります。 宣言に static
修飾子が含まれている場合、関数は 静的ローカル関数です。それ以外の場合は、 非静的ローカル関数です。 これは、 type_parameter_list または parameter_list に attributes が含まれる場合のコンパイル時エラーです。 ローカル関数が安全でないコンテキスト (§23.2) で宣言されている場合、ローカル関数の宣言に unsafe
修飾子が含まれていない場合でも、ローカル関数に安全でないコードが含まれる可能性があります。
ローカル関数はブロック スコープで宣言されます。 非静的ローカル関数は、外側のスコープから変数をキャプチャすることができますが、静的ローカル関数は含めないでください (そのため、外側のローカル、パラメーター、非静的ローカル関数、または this
にアクセスすることはできません)。 キャプチャされた変数が非静的ローカル関数の本体によって読み取られたが、関数の各呼び出しの前に確実に割り当てられない場合は、コンパイル時エラーです。 コンパイラは、どの変数が確実に戻り時に割り当てられるかを決定します (§9.4.4.33)。
this
の型が構造体型の場合、ローカル関数の本体がthis
にアクセスするためのコンパイル時エラーです。 これは、アクセスが明示的 (this.x
と同様) か暗黙的か (x
x
が構造体のインスタンス メンバーである場合) に当てはまります。 この規則では、このようなアクセスのみが禁止され、メンバー参照によって構造体のメンバーが作成されるかどうかには影響しません。
goto
ステートメント、break
ステートメント、またはターゲットがローカル関数の本体の外部にあるcontinue
ステートメントを含むのは、ローカル関数の本体のコンパイル時エラーです。
注:
this
およびgoto
に関する上記の規則は、 §12.19.3 の匿名関数の規則を反映しています。 end note
ローカル関数は、宣言の前に字句ポイントから呼び出される場合があります。 ただし、ローカル関数で使用される変数の宣言 (§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; } }
つまり、ローカル関数宣言の場所は、包含関数内のステートメントの到達可能性には影響しません。 end の例
ローカル関数の引数の型が dynamic
場合、呼び出される関数は実行時ではなくコンパイル時に解決されます。
ローカル関数は、式ツリーでは使用できません。
静的ローカル関数
- 外側のスコープから静的メンバー、型パラメーター、定数定義、静的ローカル関数を参照できます。
- 暗黙的な
this
参照からthis
またはbase
またはインスタンス メンバーを参照したり、外側のスコープからローカル変数、パラメーター、または非静的ローカル関数を参照したりしないでください。 ただし、これらのすべてが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
などの式は、ステートメントとして許可されません。 end note
expression_statementの実行は、含まれている式を評価し、制御をexpression_statementのエンドポイントに転送します。 expression_statementに到達できる場合、expression_statementの終点に到達できます。
13.8 Selection ステートメント
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(); } }
end の例
if
ステートメントは次のように実行されます。
- boolean_expression (§12.24) が評価されます。
- ブール式が
true
を生成する場合、コントロールは最初の埋め込みステートメントに転送されます。 コントロールがそのステートメントの終点に達した場合、制御はif
ステートメントのエンドポイントに転送されます。 - ブール式が
false
を生成し、else
部分が存在する場合は、2 番目の埋め込みステートメントに制御が転送されます。 コントロールがそのステートメントの終点に達した場合、制御はif
ステートメントのエンドポイントに転送されます。 - ブール式が
false
を生成し、else
部分が存在しない場合は、if
ステートメントのエンドポイントに制御が転送されます。
if
ステートメントの最初の埋め込みステートメントは、if
ステートメントに到達可能で、ブール式に定数値false
がない場合に到達できます。
if
ステートメントの 2 番目の埋め込みステートメント (存在する場合) は、if
ステートメントに到達可能で、ブール式に定数値true
がない場合に到達可能です。
if
ステートメントのエンドポイントは、その埋め込みステートメントの少なくとも 1 つのエンドポイントに到達可能な場合に到達できます。 さらに、if
ステートメントに到達可能で、ブール式に定数値がtrue
されていない場合、else
部分のないif
ステートメントのエンドポイントに到達できます。
13.8.3 switch ステートメント
switch
ステートメントは、switch 式の値に対応するスイッチ ラベルが関連付けられたステートメント リストを実行するために選択します。
switch_statement
: 'switch' '(' expression ')' switch_block
;
switch_block
: '{' switch_section* '}'
;
switch_section
: switch_label+ statement_list
;
switch_label
: 'case' pattern case_guard? ':'
| 'default' ':'
;
case_guard
: 'when' expression
;
switch_statementは、キーワード switch
の後に、かっこで囲まれた式 (switch 式と呼ばれます)、その後にswitch_blockで構成されます。 switch_blockは、0 個以上のswitch_sectionで構成され、中かっこで囲まれています。 各 switch_section は、1 つ以上の switch_labelの後に statement_list (§13.3.2) で構成されます。 case
を含む各switch_labelには、switch 式の値がテストされるパターン (§11) が関連付けられています。 case_guardが存在する場合、その式はbool
型に暗黙的に変換でき、その式はケースが満たされていると見なされる追加の条件として評価されます。
switch
ステートメントのgoverning 型は、switch 式によって確立されます。
- switch 式の型が
sbyte
、byte
、short
、ushort
、int
、uint
、long
、ulong
char
、bool
、string
、または enum_typeである場合、またはこれらの型のいずれかに対応する null 許容値型である場合は、switch
ステートメントの管理型になります。 - それ以外の場合、switch 式の型から次のいずれかの可能な制御型に対して、ユーザー定義の暗黙的な変換が 1 つだけ存在する場合:
sbyte
、byte
、short
、ushort
、int
、uint
、long
、ulong
、char
、string
、またはこれらの型のいずれかに対応する null 許容値型。変換後の型は、switch
ステートメントの制御型です。 - それ以外の場合、
switch
ステートメントの制御型は switch 式の型です。 このような型が存在しない場合はエラーです。
switch
ステートメントには、最大で 1 つのdefault
ラベルを含めることができます。
スイッチ ラベルのパターンが入力式の型 適用 (§11.2.1) でない場合は、エラーになります。
スイッチ ラベルのパターンが (§11.3) によって、ケース ガードを持たない、またはケース ガードが値 true の定数式である switch ステートメントの以前のスイッチ ラベルのパターンセットによってエラーになります。
例:
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. }
end の例
switch
ステートメントは次のように実行されます。
- switch 式が評価され、管理型に変換されます。
- 制御は、変換されたスイッチ式の値に従って転送されます。
- switch 式の値に一致し、ガード式が存在しないか true と評価される同じ
switch
ステートメント内の一連のcase
ラベルの構文上の最初のパターンにより、一致したcase
ラベルの後のステートメント リストに制御が転送されます。 - それ以外の場合、
default
ラベルが存在する場合は、default
ラベルの後のステートメント リストに制御が転送されます。 - それ以外の場合、制御は
switch
ステートメントのエンドポイントに転送されます。
- switch 式の値に一致し、ガード式が存在しないか true と評価される同じ
注: 実行時にパターンが一致する順序は定義されていません。 コンパイラは、パターンを順不同に一致させ、既に一致したパターンの結果を再利用して他のパターンの一致の結果を計算することが許可されます (必須ではありません)。 ただし、コンパイラは、式に一致し、guard 句が存在しないか、
true
に評価される構文上の最初のパターンを決定する必要があります。 end note
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; }
end の例
switch_sectionでは複数のラベルを使用できます。
例: 例
switch (i) { case 0: CaseZero(); break; case 1: CaseOne(); break; case 2: default: CaseTwo(); break; }
は有効です。 ラベル
case 2:
とdefault:
は同じ switch_sectionの一部であるため、この例は "フォールスルーなし" ルールに違反しません。end の例
注: "フォール スルーなし" ルールは、
break
ステートメントが誤って省略された場合に C および C++ で発生するバグの一般的なクラスを防ぎます。 たとえば、上記のswitch
ステートメントのセクションは、ステートメントの動作に影響を与えずに元に戻すことができます。switch (i) { default: CaseAny(); break; case 1: CaseZeroOrOne(); goto default; case 0: CaseZero(); goto case 1; }
end note
注: switch セクションのステートメント・リストは、通常、
break
、goto case
、またはgoto default
ステートメントで終わりますが、ステートメント・リストの終点を到達不能にするコンストラクトは許可されます。 たとえば、ブール式true
によって制御されるwhile
ステートメントは、そのエンドポイントに到達しないことがわかっているとします。 同様に、throw
ステートメントまたはreturn
ステートメントは常に制御を他の場所に転送し、そのエンドポイントに到達することはありません。 したがって、次の例は有効です。switch (i) { case 0: while (true) { F(); } case 1: throw new ArgumentException(); case 2: return; }
end note
例:
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; } }
end の例
注: 文字列等値演算子 (§12.12.8) と同様に、
switch
ステートメントでは大文字と小文字が区別され、switch 式の文字列がcase
ラベル定数と完全に一致する場合にのみ、特定の switch セクションが実行されます。 end noteswitch
ステートメントの制御型がstring
または null 許容値型の場合、null
値はcase
ラベル定数として許可されます。
switch_blockのstatement_listには、宣言ステートメント (§13.6) を含める場合があります。 switch ブロックで宣言されているローカル変数または定数のスコープは、switch ブロックです。
次のうち少なくとも 1 つが当てはまる場合、スイッチ ラベルに到達できます。
- switch 式は定数値であり、
- ラベルは、その値パターンが一致する (§11.2.1) を持つ
case
であり、ラベルのガード値が false の定数式がないか、または - これは
default
ラベルであり、スイッチ セクションには、その値に一致するパターンを持ち、ガードが存在しないか、値が true の定数式を持つケース ラベルが含まれています。
- ラベルは、その値パターンが一致する (§11.2.1) を持つ
- switch 式は定数値ではなく、
- ラベルは、ガードのない
case
、または値が定数 false ではないガードを持つです。 - それは
default
ラベルであり、- ガードを持たない、または値が定数 true のガードを持つ switch ステートメントのケースの中で出現するパターンのセットは、スイッチの制御の種類 (§11.4) ではありません。
- スイッチ 制御型は null 許容型であり、ガードを持たない、または値が true のガードを持つ switch ステートメントのケースの中で出現するパターンのセットには、
null
値と一致するパターンが含まれていません。
- ラベルは、ガードのない
- スイッチ ラベルは、到達可能な
goto case
またはgoto default
ステートメントによって参照されます。
特定の switch セクションのステートメント リストは、 switch
ステートメントに到達可能で、switch セクションに到達可能なスイッチ ラベルが含まれている場合に到達可能です。
switch ステートメントが到達可能であり、次の少なくとも 1 つが当てはまる場合、 switch
ステートメントのエンドポイントに到達できます。
switch
ステートメントには、switch
ステートメントを終了する到達可能なbreak
ステートメントが含まれています。default
ラベルが存在せず、いずれか- switch 式は定数以外の値であり、スイッチ ステートメントのケースの中で、ガードを持たないか、値が定数 true のガードを持つパターンのセットは、スイッチ の制御型に対して 完全ではありません (§11.4)。
- switch 式は null 許容型の非定数値であり、ガードを持たない、または定数が true のガードを持つ switch ステートメントの場合は、
null
値と一致するパターンはありません。 - switch 式は定数値であり、ガードがない
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
、空の文字列、または空白のみを含む任意の文字列と一致します。 end の例
13.9 Iteration ステートメント
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.24) が評価されます。
- ブール式が
true
を生成する場合、コントロールは埋め込みステートメントに転送されます。 コントロールが (continue
ステートメントの実行から) 埋め込みステートメントのエンドポイントに到達した場合、制御はwhile
ステートメントの先頭に転送されます。 - ブール式が
false
を生成する場合は、while
ステートメントのエンドポイントに制御が転送されます。
while
ステートメントの埋め込みステートメント内で、 break
ステートメント (§13.10.2) を使用して、while
ステートメントのエンドポイントに制御を転送できます (したがって、埋め込みステートメントの反復処理を終了します)、continue
ステートメント (§13.10.3) を使用して、埋め込みステートメントのエンドポイントに制御を転送できます (したがって、while
ステートメントのもう 1 つの反復処理を実行します)。
while
ステートメントの埋め込みステートメントに到達できるのは、while
ステートメントに到達可能で、ブール式に定数値false
がない場合です。
次の少なくとも 1 つが当てはまる場合、 while
ステートメントのエンドポイントに到達できます。
while
ステートメントには、while
ステートメントを終了する到達可能なbreak
ステートメントが含まれています。while
ステートメントに到達可能であり、ブール式には定数値true
がありません。
13.9.3 do ステートメント
do
ステートメントは、埋め込みステートメントを 1 回以上条件付きで実行します。
do_statement
: 'do' embedded_statement 'while' '(' boolean_expression ')' ';'
;
do
ステートメントは次のように実行されます。
- コントロールは埋め込みステートメントに転送されます。
- コントロールが (
continue
ステートメントの実行から) 埋め込みステートメントのエンドポイントに到達すると、 boolean_expression (§12.24) が評価されます。 ブール式がtrue
を生成する場合は、do
ステートメントの先頭に制御が転送されます。 それ以外の場合、制御はdo
ステートメントのエンドポイントに転送されます。
do
ステートメントの埋め込みステートメント内で、 break
ステートメント (§13.10.2) を使用して、do
ステートメントのエンドポイントに制御を転送できます (したがって、埋め込みステートメントの反復処理を終了します)、continue
ステートメント (§13.10.3) を使用して、埋め込みステートメントのエンドポイントに制御を転送できます (したがって、do
ステートメントのもう 1 つの反復処理を実行します)。
do
ステートメントに到達可能な場合、do
ステートメントの埋め込みステートメントに到達できます。
次の少なくとも 1 つが当てはまる場合、 do
ステートメントのエンドポイントに到達できます。
do
ステートメントには、do
ステートメントを終了する到達可能なbreak
ステートメントが含まれています。- 埋め込みステートメントの終点に到達可能であり、ブール式には定数値
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.24)。
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_condition以降) を選択します。
次のいずれかに該当する場合、 for
ステートメントの埋め込みステートメントに到達できます。
for
ステートメントに到達でき、for_conditionはありません。for
ステートメントに到達でき、for_conditionが存在し、定数値false
。
次の少なくとも 1 つが当てはまる場合、 for
ステートメントのエンドポイントに到達できます。
for
ステートメントには、for
ステートメントを終了する到達可能なbreak
ステートメントが含まれています。for
ステートメントに到達でき、for_conditionが存在し、定数値true
。
13.9.5 foreach ステートメント
foreach
ステートメントはコレクションの要素を列挙し、コレクションの各要素に対して埋め込みステートメントを実行します。
foreach_statement
: 'foreach' '(' ref_kind? local_variable_type identifier 'in'
expression ')' embedded_statement
;
foreach ステートメントの local_variable_type と identifier は、ステートメントの iteration 変数 を宣言します。 var
識別子がlocal_variable_typeとして指定され、var
という名前の型がスコープ内に存在しない場合、反復変数は単純に型指定された反復変数と呼ばれ、その型は次に示すように、foreach
ステートメントの要素型と見なされます。
foreach_statementにref
とreadonly
の両方または両方が含まれている場合、反復変数は読み取り専用として扱われる変数を表します。 それ以外の場合、foreach_statementにreadonly
のないref
が含まれている場合、反復変数は書き込み可能な変数を表します。
反復変数は、埋め込みステートメントに拡張されるスコープを持つローカル変数に対応します。 foreach
ステートメントの実行中、イテレーション変数は、イテレーションが現在実行されているコレクション要素を表します。 反復変数が読み取り専用変数を示す場合、埋め込みステートメントが (代入演算子または ++
演算子と --
演算子を使用して) 変更しようとした場合、または参照パラメーターまたは出力パラメーターとして渡そうとすると、コンパイル時エラーが発生します。
次の例では、簡潔にするために、 IEnumerable
、 IEnumerator
、 IEnumerable<T>
、および IEnumerator<T>
、 System.Collections
および System.Collections.Generic
の名前空間内の対応する型を参照します。
foreach
ステートメントのコンパイル時の処理では、まず、式のコレクション型、enumerator 型および iteration 型が決定されます。 この決定は次のように進みます。
- expressionの型
X
が配列型の場合、XからIEnumerable
インターフェイスへの暗黙的な参照変換があります(System.Array
はこのインターフェイスを実装するため)。 コレクション型はIEnumerable
インターフェイス、列挙子の型はIEnumerator
インターフェイス、反復処理の型は配列型の要素型X
。 - expression の型
X
がdynamic
場合、expressionからIEnumerable
インターフェイス (§10.2.10) への暗黙的な変換があります。 コレクション型はIEnumerable
インターフェイスであり、列挙子の型はIEnumerator
インターフェイスです。var
識別子がlocal_variable_typeとして指定されている場合、反復型はdynamic
され、それ以外の場合はobject
。 - それ以外の場合は、型
X
に適切なGetEnumerator
メソッドがあるかどうかを判断します。- 識別子
GetEnumerator
を持ち、型引数を持たない型X
に対してメンバー参照を実行します。 メンバー参照で一致が生成されない場合、またはあいまいさが生成される場合、またはメソッド グループではない一致が生成される場合は、次に説明するように列挙可能なインターフェイスを確認します。 メンバー参照でメソッド グループ以外の何かを生成する場合、または一致するものがない場合は、警告を発行することをお勧めします。 - 結果のメソッド グループと空の引数リストを使用して、オーバーロードの解決を実行します。 オーバーロードの解決によって該当するメソッドが存在しない場合、あいまいになる場合、または単一の最適なメソッドになるが、そのメソッドが静的であるかパブリックでない場合は、次に説明するように列挙可能なインターフェイスを確認します。 オーバーロードの解決で明確なパブリック インスタンス メソッド以外の何かを生成する場合、または該当するメソッドが生成されない場合は、警告を発行することをお勧めします。
GetEnumerator
メソッドの戻り値の型E
がクラス、構造体、またはインターフェイス型でない場合は、エラーが生成され、それ以上の手順は実行されません。- メンバー参照は、識別子
Current
を持つE
に対して実行され、型引数はありません。 メンバー参照で一致が生成されない場合、結果がエラーであるか、読み取りを許可するパブリック インスタンス プロパティ以外の結果である場合は、エラーが生成され、それ以上の手順は実行されません。 - メンバー参照は、識別子
MoveNext
を持つE
に対して実行され、型引数はありません。 メンバー参照で一致が生成されない場合、結果がエラーである場合、または結果がメソッド グループを除くものである場合、エラーが生成され、それ以上の手順は実行されません。 - オーバーロードの解決は、空の引数リストを使用してメソッド グループに対して実行されます。 オーバーロードの解決によって該当するメソッドが得られない場合、あいまいさが生じるか、1 つの最適なメソッドになりますが、そのメソッドが静的であるかパブリックでないか、戻り値の型が
bool
されていない場合、エラーが生成され、それ以上の手順は実行されません。 - コレクション型が
X
され、列挙子の型がE
され、反復処理の型がCurrent
プロパティの型です。Current
プロパティには、ref
修飾子を含めることができます。その場合、返される式は、必要に応じて読み取り専用のvariable_reference (§9.5) です。
- 識別子
- それ以外の場合は、列挙可能なインターフェイスを確認します。
X
からIEnumerable<Tᵢ>
への暗黙的な変換が存在するすべての型Tᵢ
、T
がdynamic
されないように一意の型T
があり、他のすべてのTᵢ
に対してIEnumerable<T>
からIEnumerable<Tᵢ>
への暗黙的な変換がある場合、コレクション型はインターフェイスIEnumerable<T>
、列挙子の型はインターフェイスIEnumerator<T>
、反復処理の種類はT
。- それ以外の場合、このような型
T
が複数存在する場合は、エラーが生成され、それ以上の手順は実行されません。 - それ以外の場合、
X
からSystem.Collections.IEnumerable
インターフェイスへの暗黙的な変換がある場合、コレクション型はこのインターフェイス、列挙子の型はインターフェイスSystem.Collections.IEnumerator
、反復処理の型はobject
。 - それ以外の場合は、エラーが生成され、それ以上の手順は実行されません。
上記の手順が成功した場合、コレクション型 C
、列挙子の型 E
、反復型の T
、 ref T
、または ref readonly T
を明確に生成します。 フォームの 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
(foreach
ステートメントのlocal_variable_type) への明示的な変換 (§10.3) がない場合は、エラーが生成され、それ以上の手順は実行されません。
反復変数が参照変数 (§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.21.3)。 T
(反復型) からV
(foreach
ステートメントのlocal_variable_type) への ID 変換 (§10.2.2) がない場合は、エラーが生成され、それ以上の手順は実行されません。
フォーム foreach (ref readonly V v in x) «embedded_statement»
のforeach
ステートメントにも同様の形式がありますが、v
参照変数は埋め込みステートメントでref readonly
されるため、再割り当てまたは再割り当てすることはできません。
注:
x
の値がnull
の場合、実行時にSystem.NullReferenceException
がスローされます。 end note
実装では、特定の foreach_statement を異なる方法で実装することが許可されます。たとえば、パフォーマンス上の理由から、動作が上記の拡張と一致している限りです。
while
ループ内でのv
の配置は、embedded_statementで発生する匿名関数によってキャプチャされる方法 (§12.19.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# がwhile
ループの外部v
宣言されていることに注意してください)。end の例
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
end の例
例: 次の例では
int[] numbers = { 1, 3, 5, 7, 9 }; foreach (var n in numbers) { Console.WriteLine(n); }
n
の型は、int
、numbers
の反復型と推定されます。end の例
13.10 ジャンプ ステートメント
13.10.1 全般
ジャンプ ステートメントは無条件に制御を転送します。
jump_statement
: break_statement
| continue_statement
| goto_statement
| return_statement
| throw_statement
;
ジャンプ ステートメントが制御を転送する場所は、jump ステートメントの target と呼ばれます。
jump ステートメントがブロック内で発生し、その jump ステートメントのターゲットがそのブロックの外側にある場合、jump ステートメントはブロックexit と言われます。 ジャンプ ステートメントはブロックから制御を転送できますが、制御をブロックに転送することはできません。
ジャンプ ステートメントの実行は、介入する try
ステートメントが存在することによって複雑になります。 このような try
ステートメントがない場合、ジャンプ ステートメントは、ジャンプ ステートメントからターゲットに無条件に制御を転送します。 このような介在する try
ステートメントが存在する場合、実行はより複雑になります。 ジャンプ ステートメントが関連付けられた finally
ブロックを持つ 1 つ以上のtry
ブロックを終了すると、制御は最初に最も内側のtry
ステートメントのfinally
ブロックに転送されます。 コントロールが finally
ブロックの終点に達すると、次に囲むステートメントの finally
ブロックに制御 try
転送されます。 このプロセスは、介在するすべてのtry
ステートメントのfinally
ブロックが実行されるまで繰り返されます。
例: 次のコード内
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 つの
try
ステートメントに関連付けられているfinally
ブロックは、制御がジャンプ ステートメントのターゲットに転送される前に実行されます。 生成される出力は次のとおりです。Before break Innermost finally block Outermost finally block After break
end の例
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) を終了できません。 finally
ブロック内でbreak
ステートメントが発生した場合、break
ステートメントのターゲットは同じfinally
ブロック内に存在する必要があります。それ以外の場合は、コンパイル時エラーが発生します。
break
ステートメントは次のように実行されます。
break
ステートメントが関連付けられたfinally
ブロックを持つ 1 つ以上のtry
ブロックを終了すると、制御は最初に最も内側のtry
ステートメントのfinally
ブロックに転送されます。 コントロールがfinally
ブロックの終点に達すると、次に囲むステートメントのfinally
ブロックに制御try
転送されます。 このプロセスは、介在するすべてのtry
ステートメントのfinally
ブロックが実行されるまで繰り返されます。- 制御は、
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) を終了できません。 finally
ブロック内でcontinue
ステートメントが発生した場合、continue
ステートメントのターゲットは同じfinally
ブロック内に存在する必要があります。それ以外の場合は、コンパイル時エラーが発生します。
continue
ステートメントは次のように実行されます。
continue
ステートメントが関連付けられたfinally
ブロックを持つ 1 つ以上のtry
ブロックを終了すると、制御は最初に最も内側のtry
ステートメントのfinally
ブロックに転送されます。 コントロールがfinally
ブロックの終点に達すると、次に囲むステートメントのfinally
ブロックに制御try
転送されます。 このプロセスは、介在するすべてのtry
ステートメントのfinally
ブロックが実行されるまで繰り返されます。- 制御は、
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
ステートメントは、入れ子になったスコープから制御を転送するために使用されます。end note
goto case
ステートメントのターゲットは、指定された定数値の定数パターンとガードなしのcase
ラベルを含む、すぐに囲む switch
ステートメント (§13.8.3) のステートメント リストです。 goto case
ステートメントがswitch
ステートメントで囲まれていない場合、最も近い外側のswitch
ステートメントにそのようなcase
が含まれていない場合、またはconstant_expressionが最も近い外側のswitch
ステートメントの制御型に暗黙的に変換できない場合 (§10.2) 場合、コンパイル時エラーが発生します。
goto default
ステートメントのターゲットは、default
ラベルを含む、すぐに囲むswitch
ステートメント (§13.8.3) 内のステートメント リストです。 goto default
ステートメントがswitch
ステートメントで囲まれていない場合、または最も近い外側のswitch
ステートメントにdefault
ラベルが含まれていない場合は、コンパイル時エラーが発生します。
goto
ステートメントは、finally
ブロック (§13.11) を終了できません。 goto
ステートメントがfinally
ブロック内で発生した場合、goto
ステートメントのターゲットは同じfinally
ブロック内にあるか、それ以外の場合はコンパイル時エラーが発生します。
goto
ステートメントは次のように実行されます。
goto
ステートメントが関連付けられたfinally
ブロックを持つ 1 つ以上のtry
ブロックを終了すると、制御は最初に最も内側のtry
ステートメントのfinally
ブロックに転送されます。 コントロールがfinally
ブロックの終点に達すると、次に囲むステートメントのfinally
ブロックに制御try
転送されます。 このプロセスは、介在するすべてのtry
ステートメントのfinally
ブロックが実行されるまで繰り返されます。- 制御は、
goto
ステートメントのターゲットに転送されます。
goto
ステートメントは無条件に制御を他の場所に転送するため、goto
ステートメントのエンドポイントに到達できません。
13.10.5 return ステートメント
return
ステートメントは、return ステートメントが出現する関数メンバーの現在の呼び出し元に制御を返し、必要に応じて値またはvariable_reference (§9.5) を返します。
return_statement
: 'return' ';'
| 'return' expression ';'
| 'return' 'ref' variable_reference ';'
;
式を持たないreturn_statementはreturn-no-valueと呼ばれ、ref
expression を含むはreturn-by-refと呼ばれ、式のみを含むは戻り値ごとに呼び出されます。
値による戻り値または returns by-ref (§15.6.1) として宣言されたメソッドからの戻り値なし値を使用するのはコンパイル時エラーです。
returns-no-value または returns-by-value として宣言されたメソッドからの return-by-ref を使用するのはコンパイル時エラーです。
returns-no-value または returns-by-ref として宣言されたメソッドから値による戻り値を使用するのはコンパイル時エラーです。
expressionがvariable_referenceでない場合、または ref-safe コンテキストが呼び出し元コンテキスト (§9.7.2) ではない変数への参照である場合は、コンパイル時エラーです。
method_modifier async
で宣言されたメソッドから ref による戻り値を使用するのはコンパイル時エラーです。
関数メンバーは 値を計算 戻り値によるメソッド (§15.6.11)、プロパティまたはインデクサーの値による戻り値取得アクセサー、またはユーザー定義演算子を持つメソッドであると言われます。 値を返さない関数メンバーは、値を計算せず、有効な戻り値の型 void
、プロパティとインデクサーのアクセサーの設定、イベント、インスタンス コンストラクター、静的コンストラクター、ファイナライザーのアクセサーの追加と削除を行うメソッドです。 ref で返される関数メンバーは、値を計算しません。
戻り値の場合、暗黙的な変換 (§10.2) は、 expression を含む関数メンバーの有効な戻り値の型 (§15.6.11) に存在する必要があります。 ref による戻り値の場合、id 変換 (§10.2.2) は、 expression 含む関数メンバーの有効な戻り値の型の間に存在する必要があります。
return
ステートメントは、匿名関数式 (§12.19) の本体でも使用でき、それらの関数に対して存在する変換の決定に関与できます (§10.7.1)。
return
ステートメントが finally
ブロックに表示されるコンパイル時エラーです (§13.11)。
return
ステートメントは次のように実行されます。
- 戻り値の場合、 expression が評価され、その値は暗黙的な変換によって包含関数の有効な戻り値の型に変換されます。 変換の結果は、関数によって生成された結果値になります。 ref による戻り値の場合、 式 が評価され、結果は変数として分類されます。 外側のメソッドの return-by-ref に
readonly
が含まれている場合、結果の変数は読み取り専用になります。 return
ステートメントが 1 つ以上のtry
または関連するfinally
ブロックを持つcatch
ブロックで囲まれている場合、制御は最初に最も内側のtry
ステートメントのfinally
ブロックに転送されます。 コントロールがfinally
ブロックの終点に達すると、次に囲むステートメントのfinally
ブロックに制御try
転送されます。 このプロセスは、外側のすべてのtry
ステートメントのfinally
ブロックが実行されるまで繰り返されます。- 包含関数が非同期関数でない場合は、制御が結果値 (存在する場合) と共に、包含関数の呼び出し元に返されます。
- 包含関数が非同期関数の場合、制御は現在の呼び出し元に返され、結果値がある場合は、(§15.15.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
ステートメントのエンドポイントに到達できません。
例外がスローされると、例外を処理できる外側の try
ステートメントの最初のcatch
句に制御が転送されます。 例外がスローされた時点から適切な例外ハンドラーに制御を転送する時点までのプロセスは、 例外伝達と呼ばれます。 例外の伝達は、例外に一致する catch
句が見つかるまで、次の手順を繰り返し評価することで構成されます。 この説明では、 位置 は、最初は例外がスローされる場所です。 この動作は (§21.4) で指定されています。
現在の関数メンバーでは、スロー ポイントを囲む各
try
ステートメントが調べされます。 ステートメントS
ごとに、最も内側のtry
ステートメントから始まり、最も外側のtry
ステートメントで終わると、次の手順が評価されます。S
のtry
ブロックがスロー ポイントを囲み、S
に 1 つ以上のcatch
句がある場合、catch
句は、例外に適したハンドラーを見つけるために外観順に調べされます。T
から派生したE
の実行時の型が一致と見なされるように、例外の種類T
(または実行時に例外の型T
を示す型パラメーター) を指定する最初のcatch
句。 句に例外フィルターが含まれている場合、例外オブジェクトが例外変数に割り当てられ、例外フィルターが評価されます。catch
句に例外フィルターが含まれている場合、そのcatch
句は、例外フィルターがtrue
と評価された場合、一致と見なされます。 一般的なcatch
(§13.11) 句は、すべての例外の種類に一致すると見なされます。 一致するcatch
句がある場合は、そのcatch
句のブロックに制御を転送することで例外伝達が完了します。- それ以外の場合、
try
ブロックまたはS
のcatch
ブロックがスロー ポイントを囲み、S
にfinally
ブロックがある場合は、finally
ブロックに制御が転送されます。finally
ブロックによって別の例外がスローされた場合、現在の例外の処理は終了されます。 それ以外の場合、制御がfinally
ブロックの終点に達すると、現在の例外の処理が続行されます。
現在の関数呼び出しで例外ハンドラーが見つからない場合、関数の呼び出しは終了し、次のいずれかが発生します。
現在の関数が非同期でない場合は、関数メンバーが呼び出されたステートメントに対応するスロー ポイントを使用して、関数の呼び出し元に対して上記の手順が繰り返されます。
現在の関数が非同期でタスクを返す場合、例外は戻りタスクに記録され、 §15.15.3 で説明されているようにエラー状態または取り消された状態。
現在の関数が非同期で
void
返される場合は、 §15.15.4 で説明されているように、現在のスレッドの同期コンテキストに通知。
例外処理が現在のスレッド内のすべての関数メンバー呼び出しを終了し、スレッドに例外のハンドラーがないことを示す場合、スレッド自体は終了します。 このような終了の影響は、実装によって定義されます。
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
型を指定する句とは意味的に異なります。 end note
例外のハンドラーを見つけるために、 catch
句は字句の順序で調べわれます。 catch
句で型を指定しても例外フィルターが指定されていない場合、同じtry
ステートメントの後のcatch
句でコンパイル時エラーが発生し、その型と同じまたは派生した型が指定されます。
注: この制限がないと、
catch
句に到達できない可能性があります。 end note
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
end の例
finally
ブロックから制御を転送するbreak
、continue
、またはgoto
ステートメントのコンパイル時エラーです。 finally
ブロックでbreak
、continue
、またはgoto
ステートメントが発生した場合、ステートメントのターゲットは同じfinally
ブロック内にあるか、コンパイル時エラーが発生します。
finally
ブロックでreturn
ステートメントが発生するのはコンパイル時エラーです。
実行が try
ステートメントに達すると、制御は try
ブロックに転送されます。 例外が伝達されずに制御が try
ブロックの終点に達した場合、制御は finally
ブロックに転送されます (存在する場合)。 finally
ブロックが存在しない場合は、try
ステートメントのエンドポイントに制御が転送されます。
例外が伝達された場合、 catch
句がある場合は、その例外に対する最初の一致を求めて字句順に調べされます。 一致する catch
句の検索は、 §13.10.6 で説明されているように、外側のすべてのブロックで続行されます。 例外の種類が任意のexception_specifierと一致し、exception_filterが true の場合、catch
句は一致します。 exception_specifierのないcatch
句は、例外の種類と一致します。 例外の種類は、exception_specifierが例外の種類または例外の種類の基本型を指定するときに、exception_specifierと一致します。 句に例外フィルターが含まれている場合、例外オブジェクトが例外変数に割り当てられ、例外フィルターが評価されます。
例外が伝達され、一致する 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
句を調べて、 例外フィルターを実行することです。 次に、Method
のfinally
句は、外側の一致するcatch
句に制御が転送される前に実行されます。 結果の出力は次のとおりです。Filter Finally Catch
end の例
try
ステートメントに到達可能な場合、try
ステートメントのtry
ブロックに到達できます。
try
ステートメントに到達可能な場合、try
ステートメントのcatch
ブロックに到達できます。
try
ステートメントに到達可能な場合、try
ステートメントのfinally
ブロックに到達できます。
次の両方に該当する場合、 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 ステートメント
using
ステートメントは、1 つ以上のリソースを取得し、ステートメントを実行してから、リソースを破棄します。
using_statement
: 'using' '(' resource_acquisition ')' embedded_statement
;
resource_acquisition
: local_variable_declaration
| expression
;
リソースは、System.IDisposable
インターフェイスを実装するクラスまたは構造体であり、Dispose
という名前のパラメーターなしのメソッドが 1 つ含まれています。 リソースを使用しているコードは、 Dispose
を呼び出して、リソースが不要であることを示すことができます。
resource_acquisitionの形式がlocal_variable_declaration場合、local_variable_declarationの型は、dynamic
または暗黙的にSystem.IDisposable
に変換できる型のいずれかになります。 resource_acquisitionの形式が expression の場合この式は暗黙的にSystem.IDisposable
に変換できます。
resource_acquisitionで宣言されたローカル変数は読み取り専用であり、初期化子を含める必要があります。 埋め込みステートメントが (代入演算子または ++
演算子と --
演算子を使用して) これらのローカル変数を変更しようとした場合、またはそれらのアドレスを参照パラメーターまたは出力パラメーターとして渡そうとすると、コンパイル時エラーが発生します。
using
ステートメントは、取得、使用、破棄の 3 つの部分に変換されます。 リソースの使用は、finally
句を含むtry
ステートメントで暗黙的に囲まれます。 この finally
句は、リソースを破棄します。 null
リソースが取得された場合、Dispose
の呼び出しは行われず、例外はスローされません。 リソースの種類がdynamic
場合は、使用と破棄の前に変換が成功するように、取得中にIDisposable
への暗黙的な動的変換 (§10.2.10) によって動的に変換されます。
フォームの using
ステートメント
using (ResourceType resource = «expression» ) «statement»
は、3 つの可能な拡張のいずれかに対応します。 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();
}
}
}
どの展開でも、 resource
変数は埋め込みステートメントでは読み取り専用であり、 d
変数は埋め込みステートメントでアクセスできなくなり、非表示になります。
特定の using_statement の実装は、パフォーマンス上の理由から、上記の拡張と一貫性がある限り、異なる方法で実装することが許可されます。
形式の using
ステートメント:
using («expression») «statement»
は、同じ 3 つの可能な拡張を持っています。 この場合、 ResourceType
は暗黙的に expression のコンパイル時の型になります (ある場合)。 それ以外の場合は、インターフェイス IDisposable
自体が ResourceType
として使用されます。 resource
変数は、埋め込まれた 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
ステートメントを使用して、書き込み操作または読み取り操作の後に基になるファイルが適切に閉じられるようにすることができます。end の例
13.15 yield ステートメント
yield
ステートメントは、反復子ブロック (§13.3) で使用され、反復子の列挙子オブジェクト (§15.14.5) または列挙可能なオブジェクト (§15.14.6) に値を返すか、イテレーションの終了を通知します。
yield_statement
: 'yield' 'return' expression ';'
| 'yield' 'break' ';'
;
yield
はコンテキスト キーワード (§6.4.4) であり、 return
または break
キーワードの直前に使用する場合にのみ特別な意味を持ちます。
次に示すように、 yield
ステートメントを使用できる場所にはいくつかの制限があります。
- method_body、operator_body、またはaccessor_bodyの外部に
yield
ステートメント (いずれかの形式) が表示されるコンパイル時エラーです。 - これは、(いずれかの形式の)
yield
ステートメントが匿名関数内に表示されるコンパイル時エラーです。 try
ステートメントのfinally
句にyield
ステートメント (いずれかの形式) が含まれる場合は、コンパイル時エラーです。- catch_clausesを含む
try
ステートメント内の任意の場所にyield return
ステートメントが表示される場合は、コンパイル時エラーです。
例: 次の例は、
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 }
end の例
暗黙的な変換 (§10.2) は、 yield return
ステートメントの式の型から反復子の yield 型 (§15.14.4) に存在する必要があります。
yield return
ステートメントは次のように実行されます。
- ステートメントで指定された式が評価され、暗黙的に yield 型に変換され、列挙子オブジェクトの
Current
プロパティに割り当てられます。 - 反復子ブロックの実行が中断されます。
yield return
ステートメントが 1 つ以上のtry
ブロック内にある場合、関連付けられているfinally
ブロックは現時点で実行されません。 - 列挙子オブジェクトの
MoveNext
メソッドは、true
を呼び出し元に返し、列挙子オブジェクトが次の項目に正常に進んだことを示します。
列挙子オブジェクトの MoveNext
メソッドの次の呼び出しは、最後に中断された場所から反復子ブロックの実行を再開します。
yield break
ステートメントは次のように実行されます。
yield break
ステートメントが、関連付けられたfinally
ブロックを持つ 1 つ以上のtry
ブロックで囲まれている場合、制御は最初に最も内側のtry
ステートメントのfinally
ブロックに転送されます。 コントロールがfinally
ブロックの終点に達すると、次に囲むステートメントのfinally
ブロックに制御try
転送されます。 このプロセスは、外側のすべてのtry
ステートメントのfinally
ブロックが実行されるまで繰り返されます。- 反復子ブロックの呼び出し元に制御が返されます。 これは、列挙子オブジェクトの
MoveNext
メソッドまたはDispose
メソッドです。
yield break
ステートメントは無条件に制御を他の場所に転送するため、yield break
ステートメントのエンドポイントに到達できません。
ECMA C# draft specification