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 (§23.2) 및 fixed_statement (§23.7)는 안전하지 않은 코드(§23)에서만 사용할 수 있습니다.
embedded_statement 비터미널은 다른 문 내에 나타나는 문에 사용됩니다. statement 대신 embedded_statement을 사용하면 이러한 컨텍스트에서 선언문과 레이블문을 사용할 수 없습니다.
예제: 코드
void F(bool b) { if (b) int i = 44; }
if
문이 해당 분기에 대한 문이 아닌 embedded_statement를 필요로 하기 때문에 컴파일 시간 오류가 발생합니다if
. 이 코드가 허용된 경우 변수i
가 선언되지만 사용할 수 없습니다. 그러나i
의 선언을 블록 안에 배치하면 예제가 유효해집니다.예제 종료
13.2 끝점 및 연결 가능성
모든 문에는 끝점이 있습니다. 직관적 측면에서 명령문의 끝점은 명령문 바로 뒤에 있는 위치입니다. 복합 문(포함된 문을 포함하는 문)에 대한 실행 규칙은 컨트롤이 포함된 문의 끝점에 도달할 때 수행되는 작업을 지정합니다.
예: 컨트롤이 블록에서 문의 끝점에 도달하면 컨트롤이 블록의 다음 문으로 전송됩니다. 예제 종료
실행으로 문에 도달할 수 있는 경우, 그 문을 도달할 수 있다고 합니다. 반대로 문장이 실행될 가능성이 없으면 해당 문장은 도달할 수 없는 문이라고 합니다.
예제: 다음 코드에서
void F() { Console.WriteLine("reachable"); goto Label; Console.WriteLine("unreachable"); Label: Console.WriteLine("reachable"); }
문이 실행될 가능성이 없으므로 Console.WriteLine의 두 번째 호출은 도달할 수 없습니다.
예제 종료
throw_statement, 블록 또는 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
호출은 실제로 실행되지 않더라도 논리적으로 도달 가능한 것으로 간주됩니다.끝 메모
함수 멤버 또는 익명 함수의 블록 은 항상 연결할 수 있는 것으로 간주됩니다. 블록에서 각 문의 연결 가능성 규칙을 연속적으로 평가하면 지정된 문의 연결 가능성을 확인할 수 있습니다.
예제: 다음 코드에서
void F(int x) { Console.WriteLine("start"); if (x < 0) Console.WriteLine("negative"); }
두 번째
Console.WriteLine
연결 가능성은 다음과 같이 결정됩니다.
- 첫 번째
Console.WriteLine
식 문은 메서드 블록F
에 도달할 수 있기 때문에 도달 가능합니다(§13.3).- 첫 번째
Console.WriteLine
식 문의 끝점이 도달 가능하기 때문에, 해당 문이 도달 가능하기 때문입니다(§13.7 및 §13.3).- 첫 번째
if
식 문의 끝점(Console.WriteLine
및 §13.3)에 도달할 수 있기 때문에 문에도 도달할 수 있습니다.- 두 번째
Console.WriteLine
식 문은if
문에 있는 부울 식에 상수 값false
이 없기 때문에 도달할 수 있습니다.예제 종료
컴파일 시 문장의 끝점이 도달 가능한 경우 오류가 발생하는 두 가지 상황이 있습니다.
switch
문이 스위치 섹션이 다음 스위치 섹션으로 "넘어가는" 것을 허용하지 않기 때문에, 스위치 섹션의 문 목록 끝점이 도달 가능하면 컴파일 시간 오류가 발생합니다. 이 오류가 발생하면 일반적으로break
문이 누락되었을 가능성을 의미합니다.값을 계산하는 함수 멤버 블록이나 익명 함수의 끝점이 도달 가능한 경우, 컴파일 시간 오류가 발생합니다. 이 오류가 발생하면 일반적으로
return
명령문이 누락되었음을 나타냅니다 (§13.10.5).
13.3 블록
13.3.1 일반
블록은 단일 문이 허용되는 컨텍스트에서 여러 문을 작성할 수 있도록 허용합니다.
block
: '{' statement_list? '}'
;
블록은 중괄호로 묶인 선택적 statement_list(§13.3.2)로 구성됩니다. 문 목록을 생략하면 블록이 비어 있다고 합니다.
블록에는 선언문(§13.6)이 포함될 수 있습니다. 블록에 선언된 지역 변수 또는 상수의 범위는 블록입니다.
블록은 다음과 같이 실행됩니다.
- 블록이 비어 있으면 컨트롤이 블록의 끝점으로 전송됩니다.
- 블록이 비어 있지 않으면 컨트롤이 문 목록으로 전송됩니다. 컨트롤이 문 목록의 끝점에 도달하면 컨트롤이 블록의 끝점으로 전송됩니다.
블록 자체에 도달 가능한 경우 블록의 문장 목록도 도달 가능합니다.
블록이 비어 있거나 문장 목록의 끝점에 도달할 수 있는 경우, 블록의 끝점에 도달할 수 있습니다.
하나 이상의 문을 포함하는 yield
(§13.15)을 반복기 블록이라고 합니다. 반복기 블록은 함수 멤버를 반복기로 구현하는 데 사용됩니다(§15.15). 반복기 블록에 몇 가지 추가 제한이 적용됩니다.
- 반복기 블록에
return
문이 나타나는 경우 컴파일 타임 오류가 발생합니다(하지만yield return
문은 허용됩니다). - 반복기 블록에 안전하지 않은 컨텍스트(§23.2)를 포함하는 것은 컴파일 시간 오류입니다. 반복기 블록은 선언이 안전하지 않은 컨텍스트에 중첩된 경우에도 항상 안전한 컨텍스트를 정의합니다.
13.3.2 문장 목록
문 목록은 순서대로 작성된 하나 이상의 문으로 구성됩니다. 문(statement) 목록은 블록 (§13.3) 및 switch_block (§13.8.3)에 존재합니다.
statement_list
: statement+
;
문 목록은 컨트롤을 첫 번째 문으로 전송하여 실행됩니다. 컨트롤이 문의 끝점에 도달하면 컨트롤이 다음 문으로 전송됩니다. 컨트롤이 마지막 문의 끝점에 도달할 때, 그리고 만약 그럴 경우, 컨트롤은 문 목록의 끝점으로 이동됩니다.
다음 중 하나 이상이 true인 경우 문 목록의 문장이 도달 가능합니다.
- 문장은 첫 번째 명령문이며, 명령문 목록 자체는 도달 가능합니다.
- 선행 문장의 끝점에 도달할 수 있습니다.
- 문은 레이블이 지정된 문이며, 도달 가능한
goto
문에서 그 레이블을 참조합니다.
목록의 마지막 문 끝점에 연결할 수 있는 경우 문 목록의 끝점에 연결할 수 있습니다.
13.4 빈 문장
empty_statement 아무 것도 하지 않습니다.
empty_statement
: ';'
;
문이 필요한 컨텍스트에서 수행할 작업이 없는 경우 빈 문이 사용됩니다.
빈 문을 실행하면 단순히 제어를 문의 끝점으로 전송합니다. 따라서, 빈 문이 도달할 수 있는 경우, 빈 문의 끝점에도 도달할 수 있습니다.
예: 본문이 없는
while
문을 작성할 때 빈 문을 사용할 수 있습니다.bool ProcessMessage() {...} void ProcessMessages() { while (ProcessMessage()) ; }
빈 문을 사용하여 블록의 마지막 "
}
" 바로 앞에 레이블을 선언할 수도 있습니다.void F(bool done) { ... if (done) { goto exit; } ... exit: ; }
예제 종료
13.5 레이블이 지정된 문
labeled_statement는 문 앞에 레이블을 붙일 수 있도록 합니다. 레이블이 지정된 문은 블록에서 허용되지만 포함된 문으로 허용되지 않습니다.
labeled_statement
: identifier ':' statement
;
레이블이 지정된 문은 식별자가 지정한 이름으로 레이블을 선언합니다. 레이블의 범위는 중첩된 블록을 포함하여 레이블이 선언되는 전체 블록입니다. 이름이 같은 두 레이블에 겹치는 범위가 있는 경우 컴파일 시간 오류입니다.
레이블 범위 내의 goto
의 문장(§13.10.4)에서 레이블을 참조할 수 있습니다.
참고: 이것은
goto
문은 블록 안팎으로 제어를 전달할 수 있지만, 블록으로 전달할 수는 없다는 것을 의미합니다. 끝 메모
레이블에는 자체 선언 공간이 있으며 다른 식별자를 방해하지 않습니다.
예제: 예제
int F(int x) { if (x >= 0) { goto x; } x = -x; x: return x; }
가 유효하고 이름 x를 매개 변수 및 레이블로 사용합니다.
예제 종료
레이블이 지정된 문의 실행은 레이블 다음에 있는 문의 실행에 정확히 해당합니다.
일반 제어 흐름에서 제공하는 도달 가능성 외에도, 레이블이 도달 가능한 goto
문에 의해 참조되는 경우, 해당 레이블이 지정된 문은 도달 가능성이 있습니다. 다만, 이 경우 goto
문이 도달 불가능한 엔드포인트를 포함하는 try
블록의 catch
의 블록 또는 finally
블록 내에 있거나, 레이블이 지정된 문이 try_statement 외부에 있지 않을 경우를 제외합니다.
13.6 선언문
13.6.1 일반
declaration_statement 하나 이상의 지역 변수, 하나 이상의 로컬 상수 또는 로컬 함수를 선언합니다. 선언 문은 블록 및 스위치 블록에서는 허용되지만, 다른 문 내에 포함된 문으로는 허용되지 않습니다.
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 하나 이상의 지역 변수를 선언합니다.
local_variable_declaration
: implicitly_typed_local_variable_declaration
| explicitly_typed_local_variable_declaration
| explicitly_typed_ref_local_variable_declaration
;
암시적으로 형식화된 선언에는 컨텍스트 키워드(§6.4.4) var
가 포함되어 있으므로 다음과 같이 확인되는 세 가지 범주 간에 구문이 모호합니다.
- 범위에 명명된
var
형식이 없고 입력이 implicitly_typed_local_variable_declaration 일치하는 경우 선택됩니다. - 그렇지 않으면 명명된
var
형식이 범위에 있으면 implicitly_typed_local_variable_declaration 가능한 일치 항목으로 간주되지 않습니다.
local_variable_declaration 내에서 각 변수는 선언자에 의해 도입되며, 이 선언자는 암시적으로 형식화된 경우 implicitly_typed_local_variable_declarator, 명시적으로 형식화된 경우 explicitly_typed_local_variable_declarator, 그리고 ref 지역 변수의 경우 ref_local_variable_declarator 중 하나입니다. 선언자는 도입된 변수의 이름(식별자) 및 초기 값(있는 경우)을 정의합니다.
선언에 여러 선언자가 있는 경우 초기화 식을 포함하여 왼쪽에서 오른쪽으로 순서대로 처리됩니다(§9.4.4.5).
참고: for_initializer(§13.9.4) 또는 resource_acquisition(§13.14)로 발생하지 않는 local_variable_declaration의 경우, 이 왼쪽에서 오른쪽 순서는 각 선언자가 별도의 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). 선언자에 초기화 식이 있는 경우 도입된 지역 변수는 선언자 끝에 할당된 것으로 분류됩니다(§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-context는 선언 블록입니다.
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는 단일 지역 변수인 식별자를 소개합니다.
식 또는 variable_reference 컴파일 시간 형식T
이 있어야 합니다. 첫 번째 대안은 식의 초기 값을 지정하여 변수를 선언합니다. 해당 형식은 T?
이(가) nullable이 아닌 참조 형식인 경우 T
이며, 그렇지 않으면 T
입니다. 두 번째 대안은 초기 값을 ref
variable_reference로 가진 ref 변수를 선언합니다. ref T?
가 nullable이 아닌 참조 형식일 때 그 변수의 형식은 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
;
explicity_typed_local_variable_declaration 지정된 형식을 가진 하나 이상의 지역 변수를 소개합니다.
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는 타입이어야 하며 ref 할당 (§12.21.3)과 동일한 요구 사항을 충족해야 합니다.
ref_kindref readonly
경우 선언되는 식별자는읽기 전용으로 처리되는 변수에 대한 참조입니다. 그렇지 않은 경우 ref_kindref
선언되는 식별자는쓰기 가능한 변수에 대한 참조입니다.
method_modifier 선언된 ref struct
메서드 내에서 또는 반복기(async
) 내에서 ref 지역 변수 또는 형식의 변수를 선언하는 것은 컴파일 시간 오류입니다.
13.6.3 로컬 상수 선언
local_constant_declaration 하나 이상의 로컬 상수가 선언됩니다.
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.23)로 구성됩니다.
로컬 상수 선언의 형식 및 constant_expression 상수 멤버 선언(§15.4)과 동일한 규칙을 따라야 합니다.
로컬 상수의 값은 simple_name 사용하여 식에서 가져옵니다(§12.8.4).
로컬 상수의 범위는 선언이 발생하는 블록입니다. constant_declarator 끝 앞에 있는 텍스트 위치에서 로컬 상수의 참조를 참조하는 것은 오류입니다.
여러 상수 선언을 선언하는 로컬 상수 선언은 동일한 형식의 단일 상수의 여러 선언과 동일합니다.
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)
예: 로컬 함수에는 반복기 메서드와 비동기 메서드의 두 가지 일반적인 사용 사례가 있습니다. 반복기 메서드에서는 반환된 시퀀스를 열거하는 코드를 호출할 때만 예외가 관찰됩니다. 비동기 메서드에서는 반환된 작업이 대기 중인 경우에만 예외가 관찰됩니다. 다음 예제에서는 로컬 함수를 사용하여 반복기 구현에서 매개 변수 유효성 검사를 분리하는 방법을 보여 줍니다.
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의 식별자는 그 선언된 블록 범위 내에서, 포함하는 지역 변수 선언 공간을 포함하여, 고유해야 합니다. 그 결과 오버로드된 local_function_declaration허용되지 않습니다.
local_function_declaration에서는 하나의 async
(§15.14) 한정자와 하나의 unsafe
(§23.1) 한정자를 포함할 수 있습니다. 선언에 async
한정자가 포함된 경우, 반환 형식은 void
또는 «TaskType»
형식(§15.14.1)이어야 합니다. 선언에 한정자가 포함된 static
경우 함수는 정적 로컬 함수이고, 그렇지 않으면 비정적 로컬 함수입니다. type_parameter_list 또는 parameter_list에 속성이 포함되면 컴파일 시간 오류가 발생합니다. 로컬 함수가 안전하지 않은 컨텍스트(§23.2)에서 선언된 경우 로컬 함수 선언에 한정자가 포함되지 않더라도 로컬 함수에 안전하지 않은 코드가 unsafe
포함될 수 있습니다.
로컬 함수는 블록 범위에서 선언됩니다. 비정적 로컬 함수는 바깥쪽 범위에서 변수를 캡처할 수 있지만 정적 로컬 함수는 그렇지 않습니다(따라서 바깥쪽 지역 함수, 매개 변수, 비정적 로컬 함수 또는 this
)에 액세스할 수 없습니다. 캡처된 변수가 비정적 로컬 함수의 본문에서 읽지만 함수를 호출하기 전에 확실히 할당되지 않은 경우 컴파일 시간 오류입니다. 컴파일러는 반환 시 확실히 할당되는 변수를 결정해야 합니다(§9.4.4.33).
형식이 this
구조체 형식인 경우, 로컬 함수의 본문에서 this
에 접근하려고 하면 컴파일 시 오류가 발생합니다. 이는 액세스가 명시적(this.x
)이든 암시적이든(x
구조체의 인스턴스 멤버인 x
)이든 마찬가지입니다. 이 규칙은 이러한 액세스만 금지하며 멤버 조회로 인해 구조체의 멤버가 생성되는지 여부에는 영향을 주지 않습니다.
로컬 함수의 본문에 대상이 로컬 함수의 본문 밖에 있는 goto
문, break
문, 또는 continue
문이 포함된다면 그것은 컴파일 시간 오류입니다.
로컬 함수는 선언 전에 어휘 지점에서 호출할 수 있습니다. 그러나 로컬 함수(§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()
이러한 모든 것이 허용됩니다.
표현식 문장
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 일반
선택 문은 일부 식의 값에 따라 실행할 수 있는 여러 문 중 하나를 선택합니다.
selection_statement
: if_statement
| switch_statement
;
13.8.2 조건문
이 문은 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.24)가 평가됩니다.
- 부울 식이
true
이면 제어가 첫 번째 내포된 문장으로 전송됩니다. 제어가 해당 문의 끝점에 도달할 때, 제어가if
문의 끝점으로 전송됩니다. - 부울 표현식이
false
를 산출하고else
부분이 있으면, 제어가 두 번째 포함된 문으로 넘어갑니다. 제어가 해당 문의 끝점에 도달할 때, 제어가if
문의 끝점으로 전송됩니다. - 부울 식이
false
를 반환하고else
부분이 없으면 제어가if
문의 끝점으로 이동합니다.
if
문의 첫 번째 포함된 문이 도달 가능하기 위해서는, if
문이 도달 가능하고 부울 식이 상수 값을 가지지 않아야 합니다.
if
문장의 두 번째 내포된 문장은, 있는 경우, if
문장이 도달 가능하고 부울 식에 상수 값 true
이 없는 경우에 도달 가능합니다.
if
의 문이 포함된 문 중 하나 이상의 끝점에 도달할 수 있는 경우 그 문장의 끝점에도 도달할 수 있습니다. 또한 if
문이 연결 가능하고 부울 식이 상수 값 else
을 가지지 않는 경우, if
부분이 없는 true
문의 끝점에 도달할 수 있습니다.
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 하나 이상의 switch_label뒤에 statement_list (§13.3.2)로 구성됩니다. 각 switch_label은 case
와 함께 포함된 스위치 식의 값이 테스트되는 연결된 패턴(§11)을 가집니다.
case_guard 있는 경우 해당 식은 형식 bool
으로 암시적으로 변환할 수 있어야 하며 해당 식은 충족된 것으로 간주될 수 있는 추가 조건으로 평가됩니다.
문장의 지배 유형switch
은 switch 식에 의해 설정됩니다.
- switch 식의 형식이
sbyte
, ,byte
,short
,,ushort
int
,uint
,long
,ulong
char
bool
string
또는 enum_type 또는 이러한 형식 중 하나에 해당하는 nullable 값 형식인 경우 해당 명령문의switch
제어 형식입니다. - 그렇지 않은 경우, 스위치 식의 형식에서 다음 가능한 지배 형식 중 하나로 정확히 하나의 사용자 정의 암시적 변환이 있을 경우:
sbyte
,byte
,short
,ushort
,int
,uint
,long
,ulong
,char
,string
, 또는 해당 형식 중 하나에 해당하는 nullable 값 형식으로 변환된 형식은switch
문의 지배형입니다. - 그렇지 않으면 문의 제어 형식
switch
이 switch 식의 형식입니다. 이러한 형식이 없으면 오류입니다.
default
문장에는 최대 하나의 switch
레이블이 있을 수 있습니다.
입력 식의 형식에 스위치 레이블의 패턴(§11.2.1)을 적용할 수 없는 경우 오류가 발생합니다.
모든 스위치 레이블의 패턴이 case guard가 없거나 case guard가 true 값을 가진 상수 식인 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
문은 다음과 같이 실행됩니다.
- switch 식이 평가되고 관리 형식으로 변환됩니다.
- 변환된 스위치 식의 값에 따라 컨트롤이 전송됩니다.
- switch 식의
case
값과 일치하고 가드 식이 없거나 true로 평가되는 동일한switch
문에 있는 레이블 집합의 어휘 첫 번째 패턴은 일치하는case
레이블 다음에 컨트롤이 문 목록으로 전송되도록 합니다. - 그렇지 않으면 레이블이
default
있으면 컨트롤이 레이블 다음에default
있는 문 목록으로 전송됩니다. - 그렇지 않으면 제어는
switch
문의 끝점으로 전송됩니다.
- switch 식의
참고: 런타임 시 패턴이 일치하는 순서는 정의되지 않습니다. 컴파일러는 순서가 맞지 않는 패턴을 일치시키고 이미 일치된 패턴의 결과를 다시 사용하여 다른 패턴의 일치 결과를 계산할 수 있습니다(필수는 아님). 그럼에도 불구하고, 어휘적으로 첫 번째로 식과 일치하며 guard 절이 없거나
true
로 평가되는 패턴을 확인하려면 컴파일러가 필요합니다. 끝 메모
스위치 섹션의 문 목록 끝 부분에 도달할 수 있으면 컴파일 시간 오류가 발생합니다. 이를 "탈락 없음" 규칙이라고 합니다.
예제: 예제
switch (i) { case 0: CaseZero(); break; case 1: CaseOne(); break; default: CaseOthers(); break; }
는 스위치 섹션에 연결할 수 있는 엔드포인트가 없으므로 유효합니다. C 및 C++와 달리 스위치 섹션의 실행은 다음 스위치 섹션으로 "넘어갈" 수 없습니다.
switch (i) { case 0: CaseZero(); case 1: CaseZeroOrOne(); default: CaseAny(); }
컴파일 시간 오류가 발생합니다. 스위치 섹션을 실행한 다음 다른 스위치 섹션을 실행하는 경우 명시적
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 일부이므로 "대체 안 함" 규칙을 위반하지 않습니다.예제 종료
참고: "폴스루 금지" 규칙은 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.12.8)와 마찬가지로,
switch
문장은 대/소문자를 구분하며 switch 표현식 문자열이 레이블 상수와 정확히 일치하는 경우에만 해당 switch 섹션을case
실행합니다. 끝 메모 문의 제어 형식switch
이string
거나 nullable 값 형식인 경우 값null
은 레이블 상수로case
허용됩니다.
switch_block의 statement_list는 선언문(§13.6)을 포함할 수 있습니다. 스위치 블록에 선언된 지역 변수 또는 상수의 범위는 스위치 블록입니다.
다음 중 하나 이상이 참인 경우 스위치 레이블에 도달할 수 있습니다.
- switch 식은 상수 값이며 또는
- 레이블은
case
패턴이 해당 값과 일치하는(§11.2.1) 경우에는 레이블의 가드가 없거나 false 값을 가지는 상수식이 아니라면; - 레이블이며
default
스위치 섹션에는 패턴이 해당 값과 일치하고 가드가 없거나 값이 true인 상수 식이 있는 사례 레이블이 포함되어 있지 않습니다.
- 레이블은
- switch 식은 상수 값이 아니며, 또한
- 레이블은
case
가드가 없거나 값이 상수 false가 아닌 가드가 있는 레이블입니다. - 이것은
default
레이블입니다.- 스위치 제어 유형에 대해 가드가 없거나 값이 상수 true인 가드가 있는 switch 문의 경우에 나타나는 패턴 집합은 완전하지 않습니다(§11.4) 또는
- 스위치 관장 형식은 널 허용 형식이며, 가드가 없거나 값이 불리언 true인 가드가 있는 switch 문의 경우에서 나타나는 패턴 집합에는 값
null
과 일치하는 패턴이 포함되어 있지 않습니다.
- 레이블은
- 스위치 레이블은
goto case
또는goto default
문으로 참조됩니다.
지정된 스위치 섹션의 문 목록은 switch
문에 접근할 수 있고, 스위치 섹션에 접근 가능한 스위치 레이블이 포함된 경우 접근할 수 있습니다.
switch
문의 끝점에 도달할 수 있는 경우는 switch 문에 도달할 수 있으며 다음 중 하나 이상이 true일 때입니다.
- 문에는
switch
문을 종료시키는 도달 가능한break
문이 포함되어 있습니다. - 레이블이 없고, 둘 중 하나가 있습니다.
- switch 식은 비상수 값이며, 가드가 없거나 가드의 값이 상수 true인 경우를 포함하는 switch 문 케이스의 패턴 집합이 스위치 제어 형식에 대해 포괄적이지 않습니다 (§11.4).
- switch 식은 nullable 형식의 비상수 값이며, true인 가드가 없는 경우나 그 값과 일치하는
null
가드가 없는 switch 문에서는 패턴이 나타나지 않습니다. - switch 표현식은 상수 값이며, 가드가 없거나 가드가 true인 어떤
case
레이블도 해당 값과 일치하지 않습니다.
예: 다음 코드는
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.24)가 평가됩니다.
- 부울 식의 값이
true
인 경우, 제어가 내재된 문장으로 전달됩니다. 포함된 문의 끝점에 제어가 도달할 때 및 도달한다면(어쩌면continue
문의 실행으로 인해), 제어는while
문의 시작 부분으로 이동합니다. - 부울 식이
false
로 평가되면, 제어가while
문의 마지막 단계로 전송됩니다.
while
문에 중첩된 문 내에서 break
문(§13.10.2)을 사용하여 제어를 while
문 끝점으로 전송할 수 있으며, 이는 중첩된 문 반복을 종료합니다. 또한, continue
문(§13.10.3)을 사용하여 중첩된 문 끝점으로 제어를 전송할 수 있어 while
문을 다시 반복 수행할 수 있습니다.
문 while
이(가) 연결 가능하고, 부울 식에 상수 값 while
이(가) 없는 경우, false
문의 포함된 문에 연결할 수 있습니다.
다음 중 하나 이상이 true인 경우 while
문의 끝점에 도달할 수 있습니다.
- 문에는
while
문을 종료시키는 도달 가능한break
문이 포함되어 있습니다. -
while
문은 도달 가능하고, 부울 식은 상수 값true
이 아닙니다.
13.9.3 do 문
이 문은 do
조건부로 포함된 문을 한 번 이상 실행합니다.
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
문을 다시 반복 수행할 수 있습니다.
do
문장이 도달할 수 있는 경우, 포함된 문장 do
문장도 도달할 수 있습니다.
다음 중 하나 이상이 true인 경우 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 있으면 변수 이니셜라이저 또는 문 식이 작성된 순서대로 실행됩니다. 이 단계는 한 번만 수행됩니다.
- 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
에서 시작하여 문의 또 다른 반복을 수행할 수 있습니다.
다음 중 하나가 true인 경우, for
명령문의 포함된 문에 도달할 수 있습니다.
-
for
문에 도달할 수 있으며 for_condition이 존재하지 않습니다. -
for
문장은 도달 가능하고 for_condition이 있으며false
라는 상수 값을 가지지 않습니다.
다음 중 하나 이상이 true인 경우 for
문의 끝점에 도달할 수 있습니다.
- 문에는
for
문을 종료시키는 도달 가능한break
문이 포함되어 있습니다. -
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 및 식별자는 문장의 반복 변수를 선언합니다.
var
식별자가 local_variable_type 지정되고 명명된 var
형식이 범위에 없는 경우 반복 변수는 암시적으로 형식화된 반복 변수라고 하며 해당 형식은 아래에 지정된 대로 문의 요소 형식 foreach
으로 간주됩니다.
await
와 ref_kind
가 모두 foreach statement
에 있을 경우 컴파일 시간 오류입니다.
foreach_statement 둘 다 포함하거나 포함하지 않는 ref
readonly
경우 반복 변수는 읽기 전용으로 처리되는 변수를 나타냅니다. 그렇지 않으면, foreach_statement에 ref
는 있지만 readonly
가 없는 경우, 반복 변수는 쓰기 가능한 변수를 나타냅니다.
반복 변수는 포함된 문을 통해 확장되는 범위가 있는 지역 변수에 해당합니다. 문을 실행하는 foreach
동안 반복 변수는 현재 반복이 수행되고 있는 컬렉션 요소를 나타냅니다. 반복 변수가 읽기 전용 변수를 나타내는 경우 포함된 문이 할당 또는 연산자를 통해 수정하거나 참조 또는 ++
--
출력 매개 변수로 전달하려고 하면 컴파일 시간 오류가 발생합니다.
문장의 컴파일 시간 처리는 먼저 식의 foreach
컬렉션 형식, 열거자 형식 및 반복 형식을 선택합니다. 문 처리 foreach
는 §13.9.5.2 에 자세히 설명되어 있으며 해당 프로세스 await foreach
는 §13.9.5.3에 자세히 설명되어 있습니다.
참고: 식에 값
null
이 있으면System.NullReferenceException
가 런타임에 발생합니다. 끝 메모
구현은 주어진 foreach_statement을 다르게 할 수 있습니다. 예를 들어 성능상의 이유로 상기 확장과 일관된 동작을 유지하는 경우라면 가능합니다.
13.9.5.2 동기 포어치
문장의 컴파일 시간 처리는 먼저 식의 foreach
컬렉션 형식, 열거자 형식 및 반복 형식을 선택합니다. 이 결정은 다음과 같이 진행됩니다.
- 식 형식이
X
배열 형식인 경우 X에서 인터페이스로의 암시적 참조 변환이 있습니다(이 인터페이스를 구현하기IEnumerable
때문에System.Array
). 컬렉션 형식은 인터페이스이고IEnumerable
열거자 형식은IEnumerator
인터페이스이고 반복 형식은 배열 형식의 요소 형식X
입니다. -
X
형식이 인 경우,dynamic
에서 식가IEnumerable
인터페이스(§10.2.10)로 암시적으로 변환됩니다. 컬렉션 형식은IEnumerable
인터페이스이고 열거자 형식은 인터페이스입니다IEnumerator
.var
식별자가 local_variable_type 지정되면 반복 형식이고dynamic
, 그렇지 않으면 다음과 같습니다object
. - 그렇지 않은 경우
X
형식에 적절한GetEnumerator
메서드가 있는지 확인합니다.- 형식 인수가 없는 형식
X
에 대해 식별자GetEnumerator
로 멤버를 조회합니다. 멤버 조회가 일치 항목을 생성하지 않거나 모호성을 생성하거나 메서드 그룹이 아닌 일치 항목을 생성하는 경우 아래 설명된 대로 열거 가능한 인터페이스를 확인합니다. 멤버 조회에서 메서드 그룹 또는 일치 항목이 아닌 다른 결과가 나올 경우 경고를 발행하는 것이 좋습니다. - 결과 메서드 그룹 및 빈 인수 목록을 사용하여 오버로드 확인을 수행합니다. 오버로드 확인으로 인해 적용 가능한 메서드가 없거나 모호성이 발생하거나 단일 최상의 메서드가 생성되지만 해당 메서드가 정적이거나 공용이 아닌 경우 아래 설명된 대로 열거 가능한 인터페이스를 확인합니다. 오버로드 확인에서 명확한 퍼블릭 인스턴스 메서드를 제외한 모든 항목이 생성되거나 적용 가능한 메서드가 없는 경우 경고를 발생시키는 것이 좋습니다.
-
E
메서드의 반환 형식GetEnumerator
클래스, 구조체 또는 인터페이스 형식이 아닌 경우 오류가 생성되고 추가 단계가 수행되지 않습니다. - 멤버 조회는 형식 인수가 없이
E
에서 식별자Current
로 수행됩니다. 멤버 조회에서 일치하는 항목이 생성되지 않거나, 결과가 오류이거나, 읽기를 허용하는 공용 인스턴스 속성을 제외한 모든 항목이 결과인 경우 오류가 생성되고 추가 단계가 수행되지 않습니다. - 멤버 조회는 형식 인수가 없이
E
에서 식별자MoveNext
로 수행됩니다. 멤버 조회에서 일치하는 항목이 생성되지 않거나, 결과가 오류이거나, 결과가 메서드 그룹을 제외한 모든 항목인 경우 오류가 생성되고 추가 단계가 수행되지 않습니다. - 오버로드 확인은 빈 인수 목록을 사용하여 메서드 그룹에서 수행됩니다. 오버로드 확인으로 인해 적용 가능한 메서드가 없거나 모호성이 발생하거나 단일 최상의 메서드가 발생하지만 해당 메서드가 정적이거나 public이 아니거나 반환 형식이 아닌 경우 오류가 생성되고 추가 단계가 수행되지 않습니다
bool
. - 컬렉션 형식이
X
, 열거자 형식이E
, 반복 형식은Current
속성의 형식입니다. 속성에는Current
한정자가 포함될ref
수 있습니다. 이 경우 반환되는 식은 선택적으로 읽기 전용인 variable_reference (§9.5)입니다.
- 형식 인수가 없는 형식
- 그렇지 않으면 열거 가능한 인터페이스를 확인합니다.
-
Tᵢ
에서X
로의 암시적 변환이 가능한 모든 형식IEnumerable<Tᵢ>
중에서,T
가T
가 아니며 다른dynamic
에 대해서도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
는 포함된 문에서 읽기 전용입니다. 명시적 변환(§10.3)이 T
(반복 형식)에서 V
( 문에서의 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
ref-reassigned(§12.21.3)해서는 안 됩니다. 정체성 변환(§10.2.2)이 T
(반복 유형)에서 V
(문의 foreach
로 변환되지 않으면 오류가 발생하고 추가 단계가 수행되지 않습니다.
foreach
폼 foreach (ref readonly V v in x) «embedded_statement»
의 문은 비슷한 형식이지만 참조 변수 v
는 ref readonly
포함된 문에 있으므로 ref-reassigned 또는 다시 할당할 수 없습니다.
v
루프 내부에 while
를 배치하는 것은 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#에서는v
가while
루프 외부에서 선언되었습니다.예제 종료
finally
블록의 본문은 다음 단계에 따라 생성됩니다.
E
의 암시적 변환이System.IDisposable
인터페이스로 되는 경우E
가 비nullable 값 형식이면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 foreach 대기
문장의 컴파일 시간 처리는 먼저 식의 foreach
컬렉션 형식, 열거자 형식 및 반복 형식을 선택합니다. 문 처리 foreach
는 §13.9.5.2 에 자세히 설명되어 있으며 해당 프로세스 await foreach
는 §13.9.5.3에 자세히 설명되어 있습니다.
이 결정은 다음과 같이 진행됩니다.
- 형식
X
에 적절한GetAsyncEnumerator
메서드가 있는지 확인합니다.- 형식 인수가 없는 형식
X
에 대해 식별자GetAsyncEnumerator
로 멤버를 조회합니다. 멤버 조회가 일치 항목을 생성하지 않거나 모호성을 생성하거나 메서드 그룹이 아닌 일치 항목을 생성하는 경우 아래 설명된 대로 열거 가능한 인터페이스를 확인합니다. 멤버 조회에서 메서드 그룹 또는 일치 항목이 아닌 다른 결과가 나올 경우 경고를 발행하는 것이 좋습니다. - 결과 메서드 그룹 및 빈 인수 목록을 사용하여 오버로드 확인을 수행합니다. 오버로드 확인으로 인해 적용 가능한 메서드가 없거나 모호성이 발생하거나 단일 최상의 메서드가 생성되지만 해당 메서드가 정적이거나 공용이 아닌 경우 아래 설명된 대로 열거 가능한 인터페이스를 확인합니다. 오버로드 확인에서 명확한 퍼블릭 인스턴스 메서드를 제외한 모든 항목이 생성되거나 적용 가능한 메서드가 없는 경우 경고를 발생시키는 것이 좋습니다.
-
E
메서드의 반환 형식GetAsyncEnumerator
클래스, 구조체 또는 인터페이스 형식이 아닌 경우 오류가 생성되고 추가 단계가 수행되지 않습니다. - 멤버 조회는 형식 인수가 없이
E
에서 식별자Current
로 수행됩니다. 멤버 조회에서 일치하는 항목이 생성되지 않거나, 결과가 오류이거나, 읽기를 허용하는 공용 인스턴스 속성을 제외한 모든 항목이 결과인 경우 오류가 생성되고 추가 단계가 수행되지 않습니다. - 멤버 조회는 형식 인수가 없이
E
에서 식별자MoveNextAsync
로 수행됩니다. 멤버 조회에서 일치하는 항목이 생성되지 않거나, 결과가 오류이거나, 결과가 메서드 그룹을 제외한 모든 항목인 경우 오류가 생성되고 추가 단계가 수행되지 않습니다. - 오버로드 확인은 빈 인수 목록을 사용하여 메서드 그룹에서 수행됩니다. 오버로드 확인으로 인해 적용 가능한 메서드가 없거나 모호성이 발생하거나 단일 최상의 메서드가 발생하지만 해당 메서드가 정적이거나 공용이 아니거나 반환 형식이 대기할 수 없거나(§12.9.8.2) await_expression (
bool
)로 분류되는 경우 오류가 생성되고 추가 단계가 수행되지 않습니다. - 컬렉션 형식이
X
, 열거자 형식이E
, 반복 형식은Current
속성의 형식입니다.
- 형식 인수가 없는 형식
- 그렇지 않으면 비동기 열거 가능한 인터페이스를 확인합니다.
-
Tᵢ
에서X
로의 암시적 변환이 가능한 모든 형식IAsyncEnumerable<Tᵢ>
중에서,T
가T
가 아니며 다른dynamic
에 대해서도Tᵢ
에서IAsyncEnumerable<T>
로의 암시적 변환이 존재하는 유일한 형식IAsyncEnumerable<Tᵢ>
가 있다면, 컬렉션 형식은 인터페이스IAsyncEnumerable<T>
이고, 열거자 형식은 인터페이스IAsyncEnumerator<T>
이며, 반복 형식은T
입니다. - 그렇지 않으면 이러한 형식이
T
두 개 이상 있는 경우 오류가 생성되고 추가 단계가 수행되지 않습니다. - 그렇지 않으면 오류가 생성되고 추가 단계가 수행되지 않습니다.
-
위의 단계는 성공적이면 컬렉션 형식 C
, 열거자 형식 E
및 반복 형식 T
명확하게 생성합니다.
await foreach
형식으로 된 문장
await foreach (V v in x) «embedded_statement»
는 다음과 같습니다.
{
E e = ((C)(x)).GetAsyncEnumerator();
try
{
while (await e.MoveNextAsync())
{
V v = (V)(T)e.Current;
«embedded_statement»
}
}
finally
{
... // Dispose e
}
}
변수 e
는 식 x
이나 포함된 문 또는 프로그램의 다른 소스 코드에 표시되거나 액세스할 수 없습니다. 변수 v
는 포함된 문에서 읽기 전용입니다. 명시적 변환(§10.3)이 T
(반복 형식)에서 V
( 문에서의 await foreach
)으로 없으면 오류가 발생하고 추가 단계는 수행되지 않습니다.
비동기 열거자는 선택적으로 인수 없이 호출할 수 있고 DisposeAsync
될 수 있는 메서드를 노출시키며, 이 메서드의 await
는 GetResult()
를 반환할 수 있습니다.
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
{
await enumerator.DisposeAsync(); // omitted, along with the try/finally,
// if the enumerator doesn't expose DisposeAsync
}
13.10 점프 문장
13.10.1 일반
점프 문은 무조건 제어를 전송합니다.
jump_statement
: break_statement
| continue_statement
| goto_statement
| return_statement
| throw_statement
;
jump 문이 컨트롤을 전송하는 위치를 jump 문의 대상 이라고 합니다.
점프 문이 블록 내에서 발생하고 해당 점프 문의 대상이 해당 블록 외부에 있는 경우, 점프 문은 블록을 종료 한다고 합니다. jump 문은 블록 외부로 제어를 전송할 수 있지만 제어를 블록으로 전송할 수 없습니다.
jump 문의 실행은 중간에 끼어드는 try
문 때문에 복잡해집니다. 이러한 try
명령문이 없는 경우 jump 문은 점프 문에서 해당 대상으로 제어를 무조건 전송합니다. 이러한 중간 try
문이 있는 경우 실행이 더 복잡합니다. jump 문이 연결된 try
블록과 함께 하나 이상의 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"); } }
제어 흐름이
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
블록과 이에 연결된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
블록과 이에 연결된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
식별자 문의 대상은 지정된 레이블을 가진 레이블 문입니다. 지정된 이름의 레이블이 현재 함수 멤버에 없거나 문이 레이블 범위 내에 없는 경우 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
블록과 이에 연결된finally
블록을 종료하는 경우, 컨트롤은 처음에 가장 안쪽finally
문의try
블록으로 전송됩니다. 컨트롤이finally
블록의 끝점에 도달할 때, 컨트롤은 다음 바깥의finally
문장의try
블록으로 전송됩니다. 이 프로세스는 모든 중간finally
문의 블록이 실행될 때까지try
반복됩니다. - 제어는
goto
문장의 대상으로 이동됩니다.
goto
문이 무조건 제어를 다른 곳으로 전송하기 때문에 goto
문 끝점이 결코 도달하지 않습니다.
13.10.5 return 문
이 문은 return
반환 문이 나타나는 함수 멤버의 현재 호출자에게 컨트롤을 반환하며, 필요에 따라 값 또는 variable_reference (§9.5)를 반환합니다.
return_statement
: 'return' ';'
| 'return' expression ';'
| 'return' 'ref' variable_reference ';'
;
식이 없는 return_statement은 반환값 없음(return-no-value)이라고 하며, ref
을 포함하는 경우는 참조로 반환(return-by-ref)이라 하고, 식만 포함하는 경우는 식을 값으로 반환(return-by-value)한다고 합니다.
반환 값을 제공하는 것으로 선언된 메서드나 반환을 참조하는 메서드(§15.6.1)에서 return-no-value를 사용하는 것은 컴파일 시간 오류입니다.
값을 반환하지 않거나 값을 반환하는 것으로 선언된 메서드에서 return-by-ref를 사용하는 것은 컴파일 시간 오류입니다.
return-no-value 또는 return-by-ref로 선언된 메서드의 반환 값을 사용하는 것은 컴파일 시간 오류입니다.
식이 variable_reference 아니거나 ref-safe-context가 호출자 컨텍스트(§9.7.2)가 아닌 변수에 대한 참조인 경우 return-by-ref를 사용하는 것은 컴파일 시간 오류입니다.
method_modifierasync
선언된 메서드의 return-by-ref를 사용하는 것은 컴파일 시간 오류입니다.
함수 멤버는 값별 반환 메서드(§15.6.11), 속성 또는 인덱서의 값별 반환 가져오기 접근자 또는 사용자 정의 연산자가 있는 메서드인 경우 값을 계산하는 것으로 합니다. 값을 반환하지 않는 함수 멤버는 값을 계산하지 않으며 유효 반환 형식을 가진 메서드, 속성 및 인덱서의 접근자 설정, 이벤트의 접근자 추가 및 제거, 인스턴스 생성자, 정적 생성자 및 종료자로 구성됩니다. return-by-ref인 함수 멤버는 값을 계산하지 않습니다.
값 반환의 경우, 식의 형식에서 포함된 함수 멤버의 유효 반환 형식(§15.6.11)으로 암시적 변환(§10.2)이 존재해야 합니다. return-by-ref의 경우 식 형식과 포함하는 함수 멤버의 유효 반환 형식 사이에 ID 변환(§10.2.2)이 있어야 합니다.
return
무명 함수 식(§12.19)의 본문에도 문을 사용하고 해당 함수에 대해 존재하는 변환을 결정하는 데 참여할 수 있습니다(§10.7.1).
return
문이 finally
블록에 나타나면 컴파일 시간 오류가 발생합니다(§13.11).
return
문은 다음과 같이 실행됩니다.
- 반환 값의 경우 식이 계산되고 해당 값이 암시적 변환을 통해 포함된 함수의 실제 반환 유형으로 변환됩니다. 변환 결과는 함수에 의해 생성된 결과 값이 됩니다. return-by-ref의 경우 식 이 평가되고 결과는 변수로 분류되어야 합니다. 바깥쪽 메서드의 return-by-ref에
readonly
가 포함된 경우에 결과 변수는 읽기 전용입니다. -
return
문이 하나 이상의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
식이 있는 문은 식을 계산하여 생성된 예외를 throw합니다. 식은 암시적으로 System.Exception
으로 변환될 수 있어야 하며, 식을 계산한 결과는 throw하기 전에 System.Exception
으로 변환됩니다. 변환의 결과가 null
이면, 대신 System.NullReferenceException
가 발생합니다.
표현식이 없는 구문은 throw
블록 안에서만 사용할 수 있습니다. 이 경우, 해당 구문은 그 블록에서 현재 처리 중인 예외를 다시 던집니다.
throw
문이 무조건 제어를 다른 곳으로 전송하기 때문에 throw
문 끝점이 결코 도달하지 않습니다.
예외가 throw되면 제어는 예외를 처리할 수 있는 감싸고 있는 catch
문장의 첫 번째 try
절로 전송됩니다. throw되는 예외 지점에서 적절한 예외 처리기로 제어를 전송하는 지점까지 발생하는 프로세스를 예외 전파라고 합니다. 예외 전파는 예외와 일치하는 절이 발견될 때까지 catch
다음 단계를 반복적으로 평가하는 것으로 구성됩니다. 이 설명에서 throw 지점은 예외가 발생하는 초기 위치를 나타냅니다. 이 동작은 (§21.4)에 지정됩니다.
현재 함수 멤버에서는 throw 지점을 포함하는 각
try
문이 검사됩니다. 각 문S
에 대해 가장try
안쪽 문으로 시작하고 가장try
바깥쪽 문으로 끝나는 다음 단계가 평가됩니다.-
try
블록이S
throw 지점을 감싸고,S
에 하나 이상의catch
절이 있는 경우,catch
절은 예외에 적합한 처리기를 찾기 위해 나타나는 순서대로 검사됩니다. 런타임 시 예외 형식catch
을 나타내는 형식 매개 변수 또는 예외 형식T
을 지정하는 첫 번째T
절에서E
의 런타임 형식이T
으로부터 파생된 것으로 간주될 때 일치한다고 봅니다. 절에 예외 필터가 포함된 경우 예외 개체가 예외 변수에 할당되고 예외 필터가 평가됩니다.catch
절에 예외 필터가 포함되어 있을 때, 예외 필터가catch
로 평가되면 해당true
절은 일치 항목으로 간주됩니다. 일반catch
(§13.11) 절은 모든 예외 형식에 대한 일치 항목으로 간주됩니다. 일치하는catch
절이 발견되면, 예외 전파는 해당 절의 블록으로 제어를 넘기는 것으로catch
완료됩니다. - 그렇지 않으면,
try
블록이나catch
블록이S
throw 지점을 포함하고, 또한S
에finally
블록이 있다면, 제어는finally
블록으로 전송됩니다.finally
블록이 다른 예외를 throw하면 현재 예외 처리가 종료됩니다. 그렇지 않으면 컨트롤이 블록의finally
끝점에 도달하면 현재 예외 처리가 계속됩니다.
-
예외 처리기가 현재 함수 호출에 없는 경우 함수 호출이 종료되고 다음 중 하나가 발생합니다.
현재 함수가 비동기인 경우 함수 멤버가 호출된 문에 해당하는 throw 지점이 있는 함수 호출자에 대해 위의 단계가 반복됩니다.
현재 함수가 비동기 및 작업 반환인 경우 예외는 반환 태스크에 기록됩니다. 이 작업은 §15.14.3에 설명된 대로 오류 또는 취소된 상태로 전환됩니다.
현재 함수가 비동기 및
void
-returning이면 §15.14.4에 설명된 대로 현재 스레드의 동기화 컨텍스트에 알림이 표시됩니다.
예외 처리가 현재 스레드의 모든 함수 멤버 호출을 종료하여 스레드에 예외 처리기가 없음을 나타내는 경우 스레드 자체는 종료됩니다. 이러한 종료의 영향은 구현에서 정의됩니다.
13.11 try 문
이 문은 try
블록을 실행하는 동안 발생하는 예외를 catch하기 위한 메커니즘을 제공합니다. 또한 문장 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
키워드와 블록, 0개 이상의 catch_clauses, 그리고 선택적인 finally_clause로 구성됩니다. 하나 이상의 catch_clause 또는 finally_clause 있어야 합니다.
exception_specifier형식 또는 유효 기본 클래스(type_parameter 경우)이거나 해당 형식에서 파생되는 형식이어야 System.Exception
합니다.
절이 catch
class_type 및 식별자를 모두 지정하면 지정된 이름과 형식의 예외 변수 가 선언됩니다. 예외 변수는 specific_catch_clause 선언 공간에 도입됩니다(§7.3).
exception_filter 및 catch
블록을 실행하는 동안 예외 변수는 현재 처리 중인 예외를 나타냅니다. 명확한 할당 검사를 위해 예외 변수는 전체 범위에서 확실히 할당된 것으로 간주됩니다.
절에 catch
예외 변수 이름이 포함되어 있지 않으면 필터 및 catch
블록의 예외 개체에 액세스할 수 없습니다.
catch
예외 형식이나 예외 변수 이름을 지정하지 않는 절을 일반 catch
절이라고 합니다. 문장에는 하나의 일반 try
절만 있을 수 있으며, 일반 catch
절이 있는 경우, 그것은 마지막 catch
절이어야 합니다.
참고: 일부 프로그래밍 언어는 C# 코드에서
System.Exception
이러한 예외를 생성할 수 없지만 파생된 개체로 나타낼 수 없는 예외를 지원할 수 있습니다. 일반catch
절을 사용하여 이러한 예외를 catch할 수 있습니다. 따라서 일반catch
절은 형식을 지정하는System.Exception
절과 의미상 다릅니다. 일반 절은 다른 언어에서 발생한 예외도 처리할 수 있습니다. 끝 메모
예외에 대한 처리기를 찾기 위해 catch
구문은 문법 순서로 검사됩니다.
catch
절이 예외 필터 없이 형식을 지정하는 경우, 동일한 catch
문의 이후 try
절에서 해당 형식과 동일하거나 파생된 형식을 지정하면 컴파일 시간 오류가 발생합니다.
참고: 이 제한이 없으면 도달할 수 없는
catch
절을 작성할 수 있습니다. 끝 메모
catch
블록 내에서는 식이 없는 throw
문(§13.10.6)을 사용하여 catch
블록에서 catch한 예외를 다시 throw할 수 있습니다. 예외 변수에 대한 할당은 다시 throw되는 예외를 변경하지 않습니다.
예제: 다음 코드에서
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
는 예외를 포착하고, 일부 진단 정보를 콘솔에 기록하며, 예외 변수를 변경하고, 예외를 다시 발생시킵니다. 다시 throw되는 예외는 원래 예외이므로 생성된 출력은 다음과 같습니다.Exception in F: G Exception in Main: G
첫 번째
catch
블록이 현재 예외를 다시 throw하지 않고e
예외를 throw했다면, 생성된 출력은 다음과 같습니다.Exception in F: G Exception in Main: F
예제 종료
break
, continue
, 또는 goto
문이 finally
블록 밖으로 컨트롤을 전송하는 것은 컴파일 시간 오류입니다. 블록 내에서 break
, continue
또는 goto
문이 발생할 때, 해당 문의 대상은 동일한 finally
블록 내에 있어야 하며, 그렇지 않으면 컴파일 시 오류가 발생합니다.
return
문이 finally
블록에서 사용할 경우, 이는 컴파일 시간 오류입니다.
try
문에 실행이 도달하면, 제어권이 try
블록으로 전송됩니다. 예외가 전파되지 않고 컨트롤이 블록의 try
끝점에 도달하면 컨트롤이 블록으로 finally
전송됩니다(있는 경우). 블록이 finally
없으면 제어가 try
문의 끝점으로 전달됩니다.
예외가 전파된 경우, catch
절이 존재한다면, 어휘 순서대로 이를 검사하여 해당 예외와 일치하는 첫 번째 항목을 찾습니다. 일치하는 catch
조건문에 대한 검색은 §13.10.6에 설명된 대로 모든 외부 블록에서 계속 진행됩니다.
catch
절은 예외 유형이 exception_specifier 중 하나에 일치하고 exception_filter가 true로 평가될 경우에 일치합니다.
catch
exception_specifier 없는 절은 예외 형식과 일치합니다. 예외 형식이 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
가 예외를 throw합니다. 첫 번째 작업은catch
를 실행하여 바깥쪽 절을 검사하는 것입니다.finally
절이 실행된 다음, 컨트롤이 바깥쪽 일치Method
절로 전송되기 전에catch
절이 실행됩니다. 결과 출력은 다음과 같습니다.Filter Finally Catch
예제 종료
try
문 블록은 try
문이 도달 가능한 경우 도달할 수 있습니다.
catch
블록은 try
문에 도달할 수 있는 경우 try
문에 도달할 수 있습니다.
finally
문 블록은 try
문이 도달 가능한 경우 도달할 수 있습니다.
다음 두 가지가 모두 참이면 try
문의 끝점에 도달할 수 있습니다.
- 블록의
try
끝점에 연결할 수 있거나 하나catch
이상의 블록의 끝점에 연결할 수 있습니다. -
finally
블록이 있으면finally
블록의 끝점에 도달할 수 있습니다.
13.12 검사된 문장과 비검사된 문장
checked
및 unchecked
문은 정수형 산술 연산 및 변환에 대한 오버플로 검사 컨텍스트를 제어하는 데 사용됩니다.
checked_statement
: 'checked' block
;
unchecked_statement
: 'unchecked' block
;
checked
문은 블록 내의 모든 식이 체크된 컨텍스트에서 계산되도록 하고, unchecked
문은 블록 내의 모든 식이 언체크된 컨텍스트에서 계산되도록 합니다.
checked
및 unchecked
문은 블록에서 작동한다는 점을 제외하고, checked
및 unchecked
연산자(§12.8.20)와 정확히 동일합니다.
13.13 lock 문장
이 문은 lock
지정된 개체에 대한 상호 배제 잠금을 가져오고 문을 실행한 다음 잠금을 해제합니다.
lock_statement
: 'lock' '(' expression ')' embedded_statement
;
식이 있는 문장은 lock
로 알려진 형식의 값을 나타냅니다. 암시적 boxing 변환(§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
한 번만 평가됩니다.
상호 배제 잠금이 유지되는 동안 동일한 실행 스레드에서 실행되는 코드는 잠금을 가져오고 해제할 수도 있습니다. 그러나 다른 스레드에서 실행되는 코드는 잠금이 해제될 때까지 잠금을 가져오지 못하도록 차단됩니다.
13.14 using 문장
이 문은 using
하나 이상의 리소스를 가져오고 문을 실행한 다음 리소스를 삭제합니다.
using_statement
: 'using' '(' resource_acquisition ')' embedded_statement
;
resource_acquisition
: local_variable_declaration
| expression
;
리소스는 (System.IDisposable
비동기 스트림의 경우) 인터페이스를 구현 IAsyncDisposable
하는 클래스 또는 구조체로, 비동기 스트림에 대해 명명된 Dispose
DisposeAsync
단일 매개 변수 없는 메서드를 포함합니다. 리소스를 사용하는 코드는 리소스가 더 이상 필요하지 않음을 나타내기 위해 호출 Dispose
할 수 있습니다.
resource_acquisition의 형식이 local_variable_declaration인 경우, local_variable_declaration의 형식은 dynamic
이거나 System.IDisposable
으로 암시적으로 변환될 수 있는 형식이어야 합니다 (IAsyncDisposable
은 비동기 스트림의 경우).
resource_acquisition 형식이 식인 경우, 이 식은 System.IDisposable
으로(비동기 스트림에서는 IAsyncDisposable
로) 암시적으로 변환 가능해야 합니다.
resource_acquisition 선언된 지역 변수는 읽기 전용이며 이니셜라이저를 포함해야 합니다. 포함된 문에서 할당 또는 연산자를 통해 이러한 지역 변수를 수정하거나, 해당 주소를 사용하거나 ++
--
, 참조 또는 출력 매개 변수로 전달하려고 하면 컴파일 시간 오류가 발생합니다.
using
문은 인수, 사용 및 폐기로 번역됩니다. 리소스 사용은 try
절이 포함된 finally
문에서 암시적으로 이루어집니다. 이 finally
절은 리소스를 삭제합니다. 리소스를 null
획득하는 경우 Dispose
호출이 이루어지지 않으며(비DisposeAsync
동기 스트림의 경우), 예외는 throw되지 않습니다. 리소스가 형식 dynamic
인 경우 사용 및 폐기 전에 변환이 성공하도록 하기 위해 인수 중에 암시적 동적 변환(§10.2.10) IDisposable
을 통해 (IAsyncDisposable
비동기 스트림의 경우) 동적으로 변환됩니다.
using
형식의 문장입니다.
using (ResourceType resource = «expression» ) «statement»
는 세 가지 가능한 확장 중 하나에 해당합니다. nullable이 아닌 값 형식 또는 값 형식 제약 조건(ResourceType
)이 있는 형식 매개 변수인 경우 확장은 의미적으로 다음과 같습니다.
{
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»
동일한 세 가지 가능한 확장이 있습니다. 이 경우 ResourceType
는 식이 컴파일 시간 형식을 가지는 경우, 암시적으로 그 컴파일 시간 형식으로 사용됩니다. 그 외에는 인터페이스 IDisposable
가 (IAsyncDisposable
비동기 스트림의 경우) 그대로 ResourceType
로 사용됩니다. 변수는 resource
포함된 문에 액세스할 수 없고 보이지 않습니다.
resource_acquisitionlocal_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 파일을 만들고 파일에 두 줄의 텍스트를 씁니다. 그런 다음 이 예제에서는 읽기 위해 동일한 파일을 열고 포함된 텍스트 줄을 콘솔에 복사합니다.
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
문을 사용하여 쓰기 또는 읽기 작업 후에 기본 파일이 제대로 닫히도록 할 수 있습니다.예제 종료
13.15 수익률 문
이 문은 yield
반복기 블록(§13.3)에서 반복기의 열거자 개체(§15.15.5) 또는 열거 가능한 개체(§15.15.6)에 값을 생성하거나 반복의 끝을 알리는 데 사용됩니다.
yield_statement
: 'yield' 'return' expression ';'
| 'yield' 'break' ';'
;
yield
는 상황별 키워드(§6.4.4)이며, return
또는 break
키워드 바로 앞에 사용될 때만 특별한 의미를 가집니다.
다음에 설명된 대로, yield
문이 나타날 수 있는 위치에는 몇 가지 제한이 있습니다.
-
yield
, 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 }
예제 종료
암시적 변환(§10.2)은 yield return
문에서 표현식의 형식과 반복자의 수확 형식(§15.15.4) 사이에 존재해야 합니다.
yield return
문은 다음과 같이 실행됩니다.
- 문에 지정된 식은 계산되고 암시적으로 수율 형식으로 변환되며 열거자 개체의 속성에
Current
할당됩니다. - 반복기 블록의 실행이 일시 중단됩니다.
yield return
문이 하나 이상의try
블록 내에 있는 경우 연결된finally
블록은 현재 실행되지 않습니다. - 열거자 개체의 메서드는
MoveNext
호출자로 반환true
되며, 이는 열거자 개체가 다음 항목으로 성공적으로 진행되었음을 나타냅니다.
열거자 개체의 MoveNext
메서드에 대한 다음 호출은 마지막으로 일시 중단된 반복기 블록의 실행을 다시 시작합니다.
yield break
문은 다음과 같이 실행됩니다.
-
yield break
문이 하나 이상의try
블록과 연결된finally
블록으로 둘러싸여 있는 경우, 제어는 처음에 가장 안쪽finally
문의try
블록으로 전송됩니다. 컨트롤이finally
블록의 끝점에 도달할 때, 컨트롤은 다음 바깥의finally
문장의try
블록으로 전송됩니다. 이 프로세스는 모든 바깥쪽finally
문의 블록이 실행될 때까지try
반복됩니다. - 컨트롤이 반복기 블록의 호출자에게 반환됩니다.
MoveNext
열거자 개체의 메서드 또는Dispose
메서드입니다.
yield break
문이 무조건 제어를 다른 곳으로 전송하기 때문에 yield break
문 끝점이 결코 도달하지 않습니다.
ECMA C# draft specification