13.1 일반
C#은 다양한 구문을 제공합니다.
참고: 이러한 명령문의 대부분은 C 및 C++로 프로그래밍한 개발자에게 익숙할 것입니다. 끝 메모
statement
: labeled_statement
| declaration_statement
| embedded_statement
;
embedded_statement
: block
| empty_statement
| expression_statement
| selection_statement
| iteration_statement
| jump_statement
| try_statement
| checked_statement
| unchecked_statement
| lock_statement
| using_statement
| yield_statement
| unsafe_statement // unsafe code support
| fixed_statement // unsafe code support
;
unsafe_statement (§24.2) 및 fixed_statement (§24.7)는 안전하지 않은 코드(§24)에서만 사용할 수 있습니다.
embedded_statement 비터미널은 다른 문 내에 나타나는 문에 사용됩니다. 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.25)의 값을 고려하지만 상수가 아닌 식의 가능한 값은 고려되지 않습니다. 즉, 제어 흐름 분석을 위해 지정된 형식의 비 상수 식은 해당 형식의 가능한 값을 갖는 것으로 간주됩니다.
예제에서
void F() { const int i = 1; if (i == 2) Console.WriteLine("unreachable"); }부울 식이
if문의 상수 식인 이유는 연산자==의 두 피연산자가 모두 상수이기 때문입니다. 상수 식이 컴파일 시간에 계산되어 값false를 생성하여,Console.WriteLine호출은 도달할 수 없는 것으로 간주됩니다. 그러나 지역 변수로 변경된 경우ivoid 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문은 허용됩니다). - 반복기 블록이 안전하지 않은 컨텍스트(§24.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입니다. 두 번째 대안은 초기 값을 refvariable_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
;
explicitly_typed_local_variable_declaration 지정된 형식을 가진 하나 이상의 지역 변수를 소개합니다.
local_variable_initializer 있는 경우 해당 형식은 단순 할당(§12.23.2) 또는 배열 초기화(§17.7)의 규칙에 따라 적절해야 하며 해당 값은 변수의 초기 값으로 할당됩니다.
13.6.2.4 명시적으로 형식화된 ref 지역 변수 선언
explicitly_typed_ref_local_variable_declaration
: ref_kind type ref_local_variable_declarators
;
ref_local_variable_declarators
: ref_local_variable_declarator (',' ref_local_variable_declarator)*
;
ref_local_variable_declarator
: identifier '=' 'ref' variable_reference
;
초기화 variable_reference형식 이 있어야 하며 ref 할당 (§12.23.3)과 동일한 요구 사항을 충족해야 합니다.
ref_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.25)로 구성됩니다.
로컬 상수 선언의 형식 및 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) 한 정자와 한정자(unsafe) 한 정자를 포함할 수 있습니다. 선언에 async 한정자가 포함된 경우, 반환 형식은 void 또는 «TaskType» 형식(§15.14.1)이어야 합니다. 선언에 한정자가 포함된 static 경우 함수는 정적 로컬 함수이고, 그렇지 않으면 비정적 로컬 함수입니다. type_parameter_list 또는 parameter_list에 속성이 포함되면 컴파일 시간 오류가 발생합니다. 로컬 함수가 안전하지 않은 컨텍스트(§24.2)에서 선언된 경우 로컬 함수 선언에 한정자가 포함되지 않더라도 로컬 함수에 안전하지 않은 코드가 unsafe 포함될 수 있습니다.
로컬 함수는 블록 범위에서 선언됩니다. 비정적 로컬 함수는 바깥쪽 범위에서 변수를 캡처할 수 있지만 정적 로컬 함수는 그렇지 않습니다(따라서 바깥쪽 지역 함수, 매개 변수, 비정적 로컬 함수 또는 this)에 액세스할 수 없습니다. 캡처된 변수가 비정적 로컬 함수의 본문에서 읽지만 함수를 호출하기 전에 확실히 할당되지 않은 경우 컴파일 시간 오류입니다. 컴파일러는 반환 시 확실히 할당되는 변수를 결정해야 합니다(§9.4.4.33).
형식이 this 구조체 형식인 경우, 로컬 함수의 본문에서 this에 접근하려고 하면 컴파일 시 오류가 발생합니다. 이는 액세스가 명시적(this.x)이든 암시적이든(x 구조체의 인스턴스 멤버인 x)이든 마찬가지입니다. 이 규칙은 이러한 액세스만 금지하며 멤버 조회로 인해 구조체의 멤버가 생성되는지 여부에는 영향을 주지 않습니다.
로컬 함수의 본문에 대상이 로컬 함수의 본문 밖에 있는 goto 문, break 문, 또는 continue 문이 포함된다면 그것은 컴파일 시간 오류입니다.
참고: 위의 규칙은
this§12.21.3의 익명 함수에 대한 규칙을 반영합니다goto. 끝 메모
로컬 함수는 선언 전에 어휘 지점에서 호출할 수 있습니다. 그러나 로컬 함수(§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.26)가 평가됩니다.
- 부울 식이
true이면 제어가 첫 번째 내포된 문장으로 전송됩니다. 제어가 해당 문의 끝점에 도달할 때, 제어가if문의 끝점으로 전송됩니다. - 부울 표현식이
false를 산출하고else부분이 있으면, 제어가 두 번째 포함된 문으로 넘어갑니다. 제어가 해당 문의 끝점에 도달할 때, 제어가if문의 끝점으로 전송됩니다. - 부울 식이
false를 반환하고else부분이 없으면 제어가if문의 끝점으로 이동합니다.
if 문의 첫 번째 포함된 문이 도달 가능하기 위해서는, if 문이 도달 가능하고 부울 식이 상수 값을 가지지 않아야 합니다.
if 문장의 두 번째 내포된 문장은, 있는 경우, if 문장이 도달 가능하고 부울 식에 상수 값 true이 없는 경우에 도달 가능합니다.
if의 문이 포함된 문 중 하나 이상의 끝점에 도달할 수 있는 경우 그 문장의 끝점에도 도달할 수 있습니다. 또한 if 문이 연결 가능하고 부울 식이 상수 값 else을 가지지 않는 경우, if 부분이 없는 true 문의 끝점에 도달할 수 있습니다.
13.8.3 switch 문장
이 문은 switch 스위치의 selector_expression 값에 해당하는 연결된 스위치 레이블이 있는 문 목록을 실행하도록 선택합니다.
switch_statement
: 'switch' selector_expression switch_block
;
selector_expression
: '(' expression ')'
| tuple_expression
;
switch_block
: '{' switch_section* '}'
;
switch_section
: switch_label+ statement_list
;
switch_label
: 'case' pattern case_guard? ':'
| 'default' ':'
;
case_guard
: 'when' null_coalescing_expression
;
switch_statement 키워드switch와 tuple_expression 또는 괄호가 있는 식(각각은 selector_expression)과 switch_block 이루어져 있습니다.
switch_block은 중괄호로 묶인 0개 이상의 switch_section으로 구성됩니다. 각 switch_section 하나 이상의 switch_label뒤에 statement_list (§13.3.2)로 구성됩니다. 포함된 case 각 switch_label 스위치의 selector_expression 값이 테스트되는 연결된 패턴(§11)을 가집니다.
case_guard 있는 경우 해당 식은 형식 bool 으로 암시적으로 변환할 수 있어야 하며 해당 식은 충족된 것으로 간주될 수 있는 추가 조건으로 평가됩니다.
참고: 편의를 위해 selector_expression tuple_expression 경우 switch_statement 괄호를 생략할 수 있습니다. 예를 들어 다음과
switch ((a, b)) …같이switch (a, b) …작성할 수 있습니다. 끝 메모
문의 제어 유형switch 은 스위치의 selector_expression 의해 설정됩니다.
- 스위치의 selector_expression 형식이
sbyte, ,byte,,short,ushortint, ,ulonglonguintboolstringchar또는 enum_type 또는 이러한 형식 중 하나에 해당하는 nullable 값 형식인 경우 해당 명령문의switch제어 형식입니다. - 그렇지 않은 경우 스위치의 selector_expression 형식에서 다음 가능한 제어 형식 중 하나로 정확히 하나의 사용자 정의 암시적 변환이 있는 경우,
sbyte, ,shortbyte,ushortint,charlongulonguintstring또는 해당 형식 중 하나에 해당하는 nullable 값 형식이 있으면 변환된 형식은 문의 관리 형식switch입니다. - 그렇지 않은 경우 문의 제어 형식
switch은 스위치의 selector_expression 형식입니다. 이러한 형식이 없으면 오류입니다.
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 문은 다음과 같이 실행됩니다.
- 스위치의 selector_expression 평가되고 관리 유형으로 변환됩니다.
- 변환된 스위치의 selector_expression 값에 따라 컨트롤이 전송됩니다.
- 스위치의
caseselector_expression 값과 일치하고 가드 식이 없거나 true로 평가되는 동일한switch문의 레이블 집합에서 어휘 첫 번째 패턴으로 인해 일치하는case레이블 다음에 컨트롤이 문 목록으로 전송됩니다. - 그렇지 않으면 레이블이
default있으면 컨트롤이 레이블 다음에default있는 문 목록으로 전송됩니다. - 그렇지 않으면 제어는
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.14.8)
switch와 마찬가지로 이 문은 대/소문자를 구분하며 스위치의 selector_expression 문자열이 레이블 상수와 정확히 일치하는 경우에만 지정된 스위치 섹션을case실행합니다. 끝 메모
문의 제어 형식 switch 이 string 거나 nullable 값 형식인 경우 값 null 은 레이블 상수로 case 허용됩니다.
switch_block의 statement_list는 선언문(§13.6)을 포함할 수 있습니다. 스위치 블록에 선언된 지역 변수 또는 상수의 범위는 스위치 블록입니다.
다음 중 하나 이상이 참인 경우 스위치 레이블에 도달할 수 있습니다.
- 스위치의 selector_expression 상수 값입니다.
- 레이블은
case패턴이 해당 값과 일치하는(§11.2.1) 경우에는 레이블의 가드가 없거나 false 값을 가지는 상수식이 아니라면; - 레이블이며
default스위치 섹션에는 패턴이 해당 값과 일치하고 가드가 없거나 값이 true인 상수 식이 있는 사례 레이블이 포함되어 있지 않습니다.
- 레이블은
- 스위치의 selector_expression 상수 값이 아니며
- 레이블은
case가드가 없거나 값이 상수 false가 아닌 가드가 있는 레이블입니다. - 이것은
default레이블입니다.- 스위치 제어 유형에 대해 가드가 없거나 값이 상수 true인 가드가 있는 switch 문의 경우에 나타나는 패턴 집합은 완전하지 않습니다(§11.4) 또는
- 스위치 관장 형식은 널 허용 형식이며, 가드가 없거나 값이 불리언 true인 가드가 있는 switch 문의 경우에서 나타나는 패턴 집합에는 값
null과 일치하는 패턴이 포함되어 있지 않습니다.
- 레이블은
- 스위치 레이블은
goto case또는goto default문으로 참조됩니다.
지정된 스위치 섹션의 문 목록은 switch 문에 접근할 수 있고, 스위치 섹션에 접근 가능한 스위치 레이블이 포함된 경우 접근할 수 있습니다.
switch 문의 끝점에 도달할 수 있는 경우는 switch 문에 도달할 수 있으며 다음 중 하나 이상이 true일 때입니다.
- 문에는
switch문을 종료시키는 도달 가능한break문이 포함되어 있습니다. - 레이블이 없고, 둘 중 하나가 있습니다.
- 스위치의 selector_expression 비 상수 값이며, switch 문에 가드가 없거나 값이 true인 가드가 없는 경우 나타나는 패턴 집합은 스위치 관리 형식에 대해 완전 하지 않습니다(§11.4).
- 스위치의 selector_expression nullable 형식의 비 상수 값이며, switch 문의 경우 가드가 없거나 값이 true인 가드가 값과 일치하는
null패턴이 나타나지 않습니다. - 스위치의 selector_expression 상수 값이며 가드가 없거나
case가드가 true인 레이블이 해당 값과 일치하지 않습니다.
예: 다음 코드는
when절을 간결하게 사용하는 방법을 보여줍니다.static object CreateShape(string shapeDescription) { switch (shapeDescription) { case "circle": return new Circle(2); … case var o when string.IsNullOrWhiteSpace(o): return null; default: return "invalid shape description"; } }var 사례는
null, 빈 문자열, 또는 공백만 포함된 문자열과 일치합니다. 예제 종료
13.9 반복 문
13.9.1 일반
반복 문은 포함된 문을 반복적으로 실행합니다.
iteration_statement
: while_statement
| do_statement
| for_statement
| foreach_statement
;
13.9.2 While 문
이 문은 while 조건부로 포함된 문을 0번 이상 실행합니다.
while_statement
: 'while' '(' boolean_expression ')' embedded_statement
;
while 문은 다음과 같이 실행됩니다.
- boolean_expression(§12.26)가 평가됩니다.
- 부울 식의 값이
true인 경우, 제어가 내재된 문장으로 전달됩니다. 포함된 문의 끝점에 제어가 도달할 때 및 도달한다면(어쩌면continue문의 실행으로 인해), 제어는while문의 시작 부분으로 이동합니다. - 부울 식이
false로 평가되면, 제어가while문의 마지막 단계로 전송됩니다.
while 문에 중첩된 문 내에서 break문(§13.10.2)을 사용하여 제어를 while문 끝점으로 전송할 수 있으며, 이는 중첩된 문 반복을 종료합니다. 또한, continue문(§13.10.3)을 사용하여 중첩된 문 끝점으로 제어를 전송할 수 있어 while문을 다시 반복 수행할 수 있습니다.
문 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.26)가 평가됩니다. 부울 식이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.26)여야 합니다.
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 둘 다 포함하거나 포함하지 않는 refreadonly경우 반복 변수는 읽기 전용으로 처리되는 변수를 나타냅니다. 그렇지 않으면, foreach_statement에 ref는 있지만 readonly가 없는 경우, 반복 변수는 쓰기 가능한 변수를 나타냅니다.
반복 변수는 포함된 문을 통해 확장되는 범위가 있는 지역 변수에 해당합니다. 문을 실행하는 foreach 동안 반복 변수는 현재 반복이 수행되고 있는 컬렉션 요소를 나타냅니다. 반복 변수가 읽기 전용 변수를 나타내는 경우 포함된 문이 할당 또는 연산자를 통해 수정하거나 참조 또는 ++-- 출력 매개 변수로 전달하려고 하면 컴파일 시간 오류가 발생합니다.
문의 컴파일 시간 처리는 먼저 식의 foreach컬렉션 형식(C), 열거자 형식(E) 및 반복 형식(Tref T또는ref readonly T)을 결정합니다.
결정은 동기 및 비동기 버전과 유사합니다. 메서드와 반환 형식이 서로 다른 인터페이스는 동기 버전과 비동기 버전을 구분합니다. 일반 프로세스는 다음과 같이 진행됩니다. '«' 및 '»' 내의 이름은 동기 및 비동기 반복기의 실제 이름에 대한 자리 표시자입니다. «GetEnumerator», «MoveNext», «IEnumerable»<T, «IEnumerator»>T< 및 기타 구분에 허용되는 형식은 동기 문의 > 및 비동 foreach 기 문의 경우 §13.9.5.3에 자세히 설명되어foreach 있습니다.
-
X형식 에 적절한 «GetEnumerator» 메서드가 있는지 확인합니다.- 식별자 «GetEnumerator» 및 형식
X인수가 없는 형식에 대한 멤버 조회를 수행합니다. 멤버 조회가 일치 항목을 생성하지 않거나 모호성을 생성하거나 메서드 그룹이 아닌 일치 항목을 생성하는 경우 2단계에서 설명한 대로 열거 가능한 인터페이스를 확인합니다. 멤버 조회에서 메서드 그룹 또는 일치 항목이 아닌 다른 결과가 나올 경우 경고를 발행하는 것이 좋습니다. - 결과 메서드 그룹 및 빈 인수 목록을 사용하여 오버로드 확인을 수행합니다. 오버로드 확인으로 인해 적용 가능한 메서드가 없거나 모호성이 발생하거나 단일 최상의 메서드가 생성되지만 해당 메서드가 정적이거나 공용이 아닌 경우 아래 설명된 대로 열거 가능한 인터페이스를 확인합니다. 오버로드 확인에서 명확한 퍼블릭 인스턴스 메서드를 제외한 모든 항목이 생성되거나 적용 가능한 메서드가 없는 경우 경고를 발생시키는 것이 좋습니다.
- «GetEnumerator» 메서드의 반환 형식
E이 클래스, 구조체 또는 인터페이스 형식이 아닌 경우 오류를 생성하고 더 이상 단계를 수행하지 않습니다. - 식별자를
E사용하여 멤버 조회Current를 수행하고 형식 인수를 수행하지 않습니다. 멤버 조회에서 일치하는 항목이 생성되지 않는 경우 결과는 오류이거나 읽기를 허용하는 공용 인스턴스 속성을 제외한 모든 결과이며 오류를 생성하고 더 이상 단계를 수행하지 않습니다. - 형식 인수가 없는 식별자 «MoveNext»를 사용하여 멤버 조회
E를 수행합니다. 멤버 조회에서 일치하는 항목이 생성되지 않는 경우 결과는 오류이거나, 결과가 메서드 그룹을 제외한 모든 항목인 경우 오류를 생성하고 더 이상 단계를 수행하지 않습니다. - 빈 인수 목록을 사용하여 메서드 그룹에서 오버로드 확인을 수행합니다. 오버로드 확인이 발생하는 경우: 적용 가능한 메서드가 없습니다. 모호성; 또는 단일 최상의 메서드이지만 해당 메서드는 정적이거나 public이 아니거나 반환 형식이 허용되는 반환 형식이 아닙니다. 그런 다음 오류를 생성하고 더 이상 단계를 수행하지 않습니다.
- 컬렉션 형식이
X, 열거자 형식이E, 반복 형식은Current속성의 형식입니다.
- 식별자 «GetEnumerator» 및 형식
- 그렇지 않으면 열거 가능한 인터페이스를 확인합니다.
- 암시적 변환이 있는 모든 형식
Tᵢ중에서 «IEnumerable»XTi<로 > 의 암시적 변환이 있는 경우, 그렇지 않은T고유한 형식Tdynamic이 있고, 다른Tᵢ모든 유형에 대해 «IEnumerable»T<에서 «IEnumerable»><Ti>로 암시적 변환이 있는 경우, 컬렉션 형식은 인터페이스 «IEnumerable»<T>, 열거자 형식은 인터페이스 «IEnumerator»<T>, 반복 유형이 .입니다T. - 그렇지 않으면 이러한 형식
T이 두 개 이상 있는 경우 오류를 생성하고 더 이상 단계를 수행하지 않습니다.
- 암시적 변환이 있는 모든 형식
참고: 식에 값
null이 있으면System.NullReferenceException가 런타임에 발생합니다. 끝 메모
구현은 지정된 foreach_statement 다르게 구현할 수 있습니다. 예를 들어 성능상의 이유로 동작이 §13.9.5.2 및 §13.9.5.3에 설명된 확장과 일치하는 경우
13.9.5.2 동기 포어치
동기 foreach 에는 키워드 앞에 키워드가 awaitforeach 포함되지 않습니다.
컬렉션 형식, 열거형 형식 및 반복 형식의 결정은 §13.9.5.1에 설명된 대로 진행됩니다. 여기서는 다음을 수행합니다.
- «GetEnumerator» 메서드입니다
GetEnumerator. - «MoveNext»는 반환 형식의
MoveNext메서드입니다bool. - «IEnumerable»<T> 는 인터페이스입니다
System.Collections.Generic.IEnumerable<T>. - «IEnumerator»<T> 는 인터페이스입니다
System.Collections.Generic.IEnumerator<T>.
또한 §13.9.5.1의 단계를 다음과 같이 수정합니다.
§13.9.5.1에 설명된 프로세스 전에 다음 단계를 수행합니다.
-
X형식이 배열 형식인 경우 배열의 요소 형식(X)인 인터페이스IEnumerable<T>로TX의 암시적 참조 변환이 있습니다. -
X형식이 인 경우,dynamic에서 식가IEnumerable인터페이스(§10.2.10)로 암시적으로 변환됩니다. 컬렉션 형식은IEnumerable인터페이스이고 열거자 형식은 인터페이스입니다IEnumerator.var식별자가 local_variable_type 지정되면 반복 형식이고dynamic, 그렇지 않으면 다음과 같습니다object.
단일 컬렉션 형식, 열거자 형식 및 반복 형식을 생성하지 않고 §13.9.5.1 의 프로세스가 완료되면 다음 단계를 수행합니다.
- 인터페이스로
XSystem.Collections.IEnumerable의 암시적 변환이 있는 경우 컬렉션 형식은 이 인터페이스이고 열거자 형식은 인터페이스System.Collections.IEnumerator이고 반복 형식은 다음과 같습니다object. - 그렇지 않으면 오류가 생성되고 추가 단계가 수행되지 않습니다.
foreach 형식의 문장입니다.
foreach (V v in x) «embedded_statement»
는 다음과 같습니다.
{
E e = ((C)(x)).GetEnumerator();
try
{
while (e.MoveNext())
{
V v = (V)(T)e.Current;
«embedded_statement»
}
}
finally
{
... // Dispose e
}
}
변수 e 는 식 x 이나 포함된 문 또는 프로그램의 다른 소스 코드에 표시되거나 액세스할 수 없습니다. 변수 v 는 포함된 문에서 읽기 전용입니다. 명시적 변환(§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.23.3)는 안 됩니다. 정체성 변환(§10.2.2)이 T (반복 유형)에서 V (문의 foreach로 변환되지 않으면 오류가 발생하고 추가 단계가 수행되지 않습니다.
foreach 폼 foreach (ref readonly V v in x) «embedded_statement» 의 문은 비슷한 형식이지만 참조 변수 v 는 ref readonly 포함된 문에 있으므로 ref-reassigned 또는 다시 할당할 수 없습니다.
루프 내부 while 배치 v 는 embedded_statement 발생하는 익명 함수에 의해 캡처되는 방식(§12.21.6.2)에 중요합니다.
예제:
int[] values = { 7, 9, 13 }; Action f = null; foreach (var value in values) { if (f == null) { f = () => Console.WriteLine("First value: " + value); } } f();확장된 형태에서
v가while루프 외부에서 선언되면, 이는 모든 반복에서 공유되며,for루프가 끝난 후의 값은 최종 값13이 됩니다. 이 값은f의 호출 시 인쇄되는 값입니다. 대신 각 반복에 고유한 변수v가 있으므로 첫 번째 반복에서 캡처한f변수는 계속 값을 저장합니다. 이 값7은 인쇄됩니다. 이전 버전의 C#에서는v가while루프 외부에서 선언되었습니다.예제 종료
finally 블록의 본문은 다음 단계에 따라 생성됩니다.
E의 암시적 변환이System.IDisposable인터페이스로 되는 경우E가 비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는 await foreach 구문을 사용합니다.
컬렉션 형식, 열거형 형식 및 반복 형식의 결정은 §13.9.5.1에 설명된 대로 진행됩니다. 여기서는 다음을 수행합니다.
- «GetEnumerator»는 대기 가능한 반환 형식(
GetEnumeratorAsync)을 갖는 메서드입니다. - «MoveNext»는
MoveNextAsync대기 가능한 반환 형식(§12.9.9.2)이 있는 메서드로, await_expression (bool)로 분류됩니다. - «IEnumerable»<T> 는 인터페이스입니다
System.Collections.Generic.IAsyncEnumerable<T>. - «IEnumerator»<T> 는 인터페이스입니다
System.Collections.Generic.IAsyncEnumerator<T>.
문 반복 형식await foreach 이 참조 변수(§9.7)가 되는 것은 오류입니다.
await foreach 형식으로 된 문장
await foreach (T item in enumerable) «embedded_statement»
의미 체계는 다음과 같습니다.
var enumerator = enumerable.GetAsyncEnumerator();
try
{
while (await enumerator.MoveNextAsync())
{
T item = enumerator.Current;
«embedded_statement»
}
}
finally
{
// dispose of enumerator as described later in this clause.
}
식 enumerable 이 메서드 호출 식을 나타내고 매개 변수 중 하나가 (EnumeratorCancellationAttribute)로 표시된 CancellationToken 경우 메서드에 GetAsyncEnumerator 전달됩니다. 다른 라이브러리 메서드는 CancellationToken 에 전달되어야 할 GetAsyncEnumerator수 있습니다. 이러한 메서드가 식enumerable의 일부인 경우 토큰은 마치 by와 해당 CreateLinkedTokenSource 속성처럼 Token 단일 토큰으로 결합되어야 합니다.
finally 블록의 본문은 다음 단계에 따라 생성됩니다.
반환 형식이 대기 가능한 액세스 가능한
E메서드(DisposeAsync())가 있는 경우finally절은 다음과 같은 의미 체계로 확장됩니다.finally { await e.DisposeAsync(); }그렇지 않은 경우 인터페이스로
ESystem.IAsyncDisposable의 암시적 변환이 있고Enullable이 아닌 값 형식finally인 경우 절은 다음과 같은 의미 체계로 확장됩니다.finally { await ((System.IAsyncDisposable)e).DisposeAsync(); }단,
E가 값 형식이거나 값 형식으로 인스턴스화된 형식 매개 변수인 경우e을System.IAsyncDisposable로 변환해도 박싱이 발생하지 않습니다.그렇지 않으면 형식이고
E액세스 가능한ref struct메서드Dispose()가 있는 경우finally절은 다음과 같은 의미 체계로 확장됩니다.finally { e.Dispose(); }그렇지 않으면
E이(가) 봉인된 형식인 경우에finally절이 빈 블록으로 확장됩니다.finally {}그렇지 않으면 절이
finally다음으로 확장됩니다.finally { System.IAsyncDisposable d = e as System.IAsyncDisposable; if (d != null) { await d.DisposeAsync(); } }
지역 변수 d 는 사용자 코드에 표시되거나 액세스할 수 없습니다. 특히 범위가 블록을 포함하는 finally 다른 변수와 충돌하지 않습니다.
참고:
await foreach비동기 삭제 메커니즘을e사용할 수 없는 경우 동기적으로 삭제할 필요가 없습니다. 끝 메모
13.10 점프 문장
13.10.1 일반
점프 문은 무조건 제어를 전송합니다.
jump_statement
: break_statement
| continue_statement
| goto_statement
| return_statement
| throw_statement
;
jump 문이 컨트롤을 전송하는 위치를 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.21)의 본문에도 문을 사용하고 해당 함수에 대해 존재하는 변환을 결정하는 데 참여할 수 있습니다(§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 지점은 예외가 발생하는 초기 위치를 나타냅니다. 이 동작은 (§22.4)에 지정됩니다.
현재 함수 멤버에서는 throw 지점을 포함하는 각
try문이 검사됩니다. 각 문S에 대해 가장try안쪽 문으로 시작하고 가장try바깥쪽 문으로 끝나는 다음 단계가 평가됩니다.-
try블록이Sthrow 지점을 감싸고,S에 하나 이상의catch절이 있는 경우,catch절은 예외에 적합한 처리기를 찾기 위해 나타나는 순서대로 검사됩니다. 런타임 시 예외 형식catch을 나타내는 형식 매개 변수 또는 예외 형식T을 지정하는 첫 번째T절에서E의 런타임 형식이T으로부터 파생된 것으로 간주될 때 일치한다고 봅니다. 절에 예외 필터가 포함된 경우 예외 개체가 예외 변수에 할당되고 예외 필터가 평가됩니다.catch절에 예외 필터가 포함되어 있을 때, 예외 필터가catch로 평가되면 해당true절은 일치 항목으로 간주됩니다. 일반catch(§13.11) 절은 모든 예외 형식에 대한 일치 항목으로 간주됩니다. 일치하는catch절이 발견되면, 예외 전파는 해당 절의 블록으로 제어를 넘기는 것으로catch완료됩니다. - 그렇지 않으면,
try블록이나catch블록이Sthrow 지점을 포함하고, 또한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 합니다.
절이 catchclass_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와 일치합니다. 절에 예외 필터가 포함된 경우 예외 개체가 예외 변수에 할당되고 예외 필터가 평가됩니다.
exception_filter 대한 boolean_expression 평가에서 예외가 throw되면 해당 예외가 catch되고 예외 필터가 계산됩니다false.
예외가 전파되고 일치하는 catch 절이 발견되면 컨트롤이 첫 번째 일치 catch 블록으로 전송됩니다. 예외가 전파되지 않고 컨트롤이 블록의 catch 끝점에 도달하면 컨트롤이 블록으로 finally 전송됩니다(있는 경우). 블록이 finally 없으면 제어가 try 문의 끝점으로 전달됩니다.
catch 블록에서 예외가 전파된 경우, finally 블록이 존재하면 그쪽으로 제어가 전송됩니다. 예외는 다음으로 둘러싸고 있는 try 문으로 전파됩니다.
예외가 전파되고 일치하는 catch 절을 찾을 수 없는 경우 제어가 finally 블록으로 전송됩니다(있는 경우). 예외는 다음으로 둘러싸고 있는 try 문으로 전파됩니다.
finally 블록의 문은 제어가 try 문을 벗어날 때 항상 실행됩니다. 컨트롤 전송은 일반 실행의 결과로 발생하든, break, continue, goto, 또는 return 문을 실행한 결과로 발생하든, 혹은 try 문에서 예외 전파의 결과로 발생하든 상관없이 동일하게 적용됩니다. 예외가 전파되지 않고 컨트롤이 finally 블록의 끝점에 도달하면 제어가 문의 끝점으로 try 전송됩니다.
finally 블록을 실행하는 동안 예외가 발생하여 동일한 finally 블록 내에서 처리되지 않으면, 그 예외는 다음에 포함된 try 문으로 전파됩니다. 다른 예외가 전파되는 중이면 해당 예외가 손실됩니다. 예외를 전파하는 프로세스는 문 설명 throw (§13.10.6)에서 자세히 설명합니다.
예제: 다음 코드에서
public class Test { static void Main() { try { Method(); } catch (Exception ex) when (ExceptionFilter(ex)) { Console.WriteLine("Catch"); } bool ExceptionFilter(Exception ex) { Console.WriteLine("Filter"); return true; } } static void Method() { try { throw new ArgumentException(); } finally { Console.WriteLine("Finally"); } } }메서드
Method가 예외를 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 문장
13.14.1 일반
이 문은 using 하나 이상의 리소스를 가져오고 문을 실행한 다음 리소스를 삭제합니다.
using_statement
: 'await'? 'using' '(' resource_acquisition ')' embedded_statement
;
resource_acquisition
: non_ref_local_variable_declaration
| expression
;
non_ref_local_variable_declaration
: implicitly_typed_local_variable_declaration
| explicitly_typed_local_variable_declaration
;
리소스 형식은 명명된 단일 매개 변수 없는 메서드 System.IDisposable 및/또는 ; 또는 선언된 것과 동일한 시그니처System.IAsyncDisposable를 갖는 메서드 Dispose 를 포함하는 ref 구조체를 포함하는 또는 인터페이스 중 DisposeAsync 하나 또는 Dispose 둘 다를 구현하는 클래스 또는 System.IDisposable비-ref 구조체입니다. 리소스를 사용하는 코드는 리소스가 더 이상 필요하지 않음을 호출 Dispose 하거나 DisposeAsync 나타낼 수 있습니다.
resource_acquisition 형식이 local_variable_declaration 경우 local_variable_declaration 형식은 리소스 유형이어야 dynamic 합니다.
resource_acquisition 형식이 식인 경우 이 식에는 리소스 종류가 있어야 합니다. 있는 경우 await 리소스 종류는 구현 System.IAsyncDisposable해야 합니다. 형식은 한 ref struct 정자가 있는 문의 using 리소스 형식일 await 수 없습니다.
resource_acquisition 선언된 지역 변수는 읽기 전용이며 이니셜라이저를 포함해야 합니다. 포함된 문에서 할당 또는 연산자를 통해 이러한 지역 변수를 수정하거나, 해당 주소를 사용하거나 ++-- , 참조 또는 출력 매개 변수로 전달하려고 하면 컴파일 시간 오류가 발생합니다.
using 문은 인수, 사용 및 폐기로 번역됩니다. 리소스 사용은 try 절이 포함된 finally 문에서 암시적으로 이루어집니다. 이 finally 절은 리소스를 삭제합니다. 인수 식이 계산 null되면 호출 Dispose (또는 DisposeAsync)이 발생하지 않고 예외가 throw되지 않습니다. 리소스가 형식 dynamic 인 경우 사용 및 폐기 전에 변환이 성공하도록 하기 위해 인수 중에 암시적 동적 변환(§10.2.10)IDisposableIAsyncDisposable을 통해 동적으로 변환됩니다.
using 형식의 문장입니다.
using (ResourceType resource = «expression» ) «statement»
는 가능한 세 가지 공식 중 하나에 해당합니다. 클래스 및 비-ref 구조체 리소스의 경우 nullable이 아닌 값 형식 또는 값 형식 제약 조건(ResourceType)이 있는 형식 매개 변수인 경우 이 수식은 의미상
{
ResourceType resource = «expression»;
try
{
«statement»;
}
finally
{
((IDisposable)resource).Dispose();
}
}
resource를 System.IDisposable로 형변환할 때 박싱이 발생하지 않는다는 점을 예외로 합니다.
그렇지 않은 경우 ResourceTypedynamic, 제형은 다음과 입니다.
{
ResourceType resource = «expression»;
IDisposable d = resource;
try
{
«statement»;
}
finally
{
if (d != null)
{
d.Dispose();
}
}
}
그렇지 않으면 공식은
{
ResourceType resource = «expression»;
try
{
«statement»;
}
finally
{
IDisposable d = (IDisposable)resource;
if (d != null)
{
d.Dispose();
}
}
}
ref 구조체 리소스의 경우 의미상 동등한 공식은
{
«ResourceType» resource = «expression»;
try
{
«statement»;
}
finally
{
resource.Dispose();
}
}
모든 공식 resource 에서 변수는 포함된 문에서 읽기 전용이며 d , 포함된 문에서 변수에 액세스할 수 없고 보이지 않습니다.
using 형태의 문장:
using («expression») «statement»
에는 동일한 가능한 공식이 있습니다.
resource_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문을 사용하여 쓰기 또는 읽기 작업 후에 기본 파일이 제대로 닫히도록 할 수 있습니다.예제 종료
를 구현하는 참조 형식은 언제 ResourceType 인가요?IAsyncDisposable 동기 메서드에서 비 await using 동 Dispose 기 메서드로 유사한 대체를 수행하기 위한 DisposeAsync 다른 공식입니다.
await using 형식으로 된 문장
await using (ResourceType resource = «expression» ) «statement»
은 의미상 아래에 표시된 공식과 의미상 같으며 IAsyncDisposableIDisposableDisposeAsync, Dispose 반환되는 형식은 Tasked가 됩니다.DisposeAsyncawait
await using (ResourceType resource = «expression» ) «statement»
의미 체계는
{
ResourceType resource = «expression»;
try
{
«statement»;
}
finally
{
IAsyncDisposable d = (IAsyncDisposable)resource;
if (d != null)
{
await d.DisposeAsync();
}
}
}
참고: embedded_statement 모든 jump 문(§13.10)은 확장된 형태의
using문을 준수해야 합니다. 끝 메모
13.14.2 선언 사용
using 문의 구문 변형은 using 선언입니다.
using_declaration
: 'await'? 'using' non_ref_local_variable_declaration ';' statement_list?
;
using 선언은 다음과 같은 의미 체계를 가지며 using 문(§13.14.1)의 해당 리소스 획득 형식으로 다시 작성할 수 있습니다.
using «local_variable_type» «local_variable_declarators»
// statements
의미 체계는
using («local_variable_type» «local_variable_declarators»)
{
// statements
}
및
await using «local_variable_type» «local_variable_declarators»
// statements
의미 체계는
await using («local_variable_type» «local_variable_declarators»)
{
// statements
}
non_ref_local_variable_declaration 선언된 변수의 수명은 선언된 범위의 끝까지 확장됩니다. 그런 다음 이러한 변수는 선언된 역순으로 삭제됩니다.
static void M()
{
using FileStream f1 = new FileStream(...);
using FileStream f2 = new FileStream(...), f3 = new FileStream(...);
...
// Dispose f3
// Dispose f2
// Dispose f1
}
using 선언은 switch_label 내부에 직접 나타나지 않고 대신 switch_label 내의 블록 내에 있을 수 있습니다.
13.15 수익률 문
이 문은 yield 반복기 블록(§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