13개 문
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 비터미널은 다른 문 내에 나타나는 문에 사용됩니다. 문 대신 embedded_statement 사용하면 이러한 컨텍스트에서 선언 문과 레이블이 지정된 문의 사용이 제외됩니다.
예제: 코드
void F(bool b) { if (b) int i = 44; }
에서는 해당 분기에 대한 문이 아닌 embedded_statement 필요하므로 컴파일 시간 오류가
if
발생합니다if
. 이 코드가 허용된 경우 변수i
가 선언되지만 사용할 수 없습니다. 그러나 블록에 's 선언을 배치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
연결 가능성은 다음과 같이 결정됩니다.
- 메서드 블록
F
에 연결할 수 있으므로 첫 번째Console.WriteLine
식 문에 연결할 수 있습니다(§13.3).- 첫 번째
Console.WriteLine
식 문의 끝점에 연결할 수 있습니다(§13.7 및 §13.3).if
첫 번째Console.WriteLine
식 문의 끝점(§13.7 및 §13.3)에 도달할 수 있으므로 문에 연결할 수 있습니다.- 문의 부울 식에 상수 값
false
이if
없으므로 두 번째Console.WriteLine
식 문에 연결할 수 있습니다.끝 예제
문의 끝점에 연결할 수 있는 컴파일 시간 오류인 두 가지 상황이 있습니다.
이 문은
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.14). 반복기 블록에 몇 가지 추가 제한이 적용됩니다.
- 문이 반복기 블록에 표시되는 것은 컴파일 시간 오류
return
입니다(하지만yield return
문은 허용됨). - 반복기 블록이 안전하지 않은 컨텍스트(§23.2)를 포함하는 것은 컴파일 시간 오류입니다. 반복기 블록은 선언이 안전하지 않은 컨텍스트에 중첩된 경우에도 항상 안전한 컨텍스트를 정의합니다.
13.3.2 문 목록
문 목록은 순서대로 작성된 하나 이상의 문으로 구성됩니다. 문 목록은 블록s(§13.3) 및 switch_block(§13.8.3)에서 발생합니다.
statement_list
: statement+
;
문 목록은 컨트롤을 첫 번째 문으로 전송하여 실행됩니다. 컨트롤이 문의 끝점에 도달하면 컨트롤이 다음 문으로 전송됩니다. 컨트롤이 마지막 문의 끝점에 도달하면 컨트롤이 문 목록의 끝점으로 전송됩니다.
다음 중 하나 이상이 true인 경우 문 목록의 문에 연결할 수 있습니다.
- 문은 첫 번째 문이며 문 목록 자체에 연결할 수 있습니다.
- 이전 문의 끝점에 연결할 수 있습니다.
- 문은 레이블이 지정된 문이며 레이블은 도달 가능한
goto
문으로 참조됩니다.
목록의 마지막 문 끝점에 연결할 수 있는 경우 문 목록의 끝점에 연결할 수 있습니다.
13.4 빈 문
empty_statement 아무 것도 하지 않습니다.
empty_statement
: ';'
;
문이 필요한 컨텍스트에서 수행할 작업이 없는 경우 빈 문이 사용됩니다.
빈 문을 실행하면 단순히 제어를 문의 끝점으로 전송합니다. 따라서 빈 문에 연결할 수 있는 경우 빈 문의 끝점에 연결할 수 있습니다.
예: null 본문으로 문을 작성
while
할 때 빈 문을 사용할 수 있습니다.bool ProcessMessage() {...} void ProcessMessages() { while (ProcessMessage()) ; }
또한 빈 문을 사용하여 블록의 닫는 "
}
" 바로 앞에 레이블을 선언할 수 있습니다.void F(bool done) { ... if (done) { goto exit; } ... exit: ; }
끝 예제
13.5 레이블이 지정된 문
labeled_statement 레이블 앞에 문 접두사를 지정할 수 있습니다. 레이블이 지정된 문은 블록에서 허용되지만 포함된 문으로 허용되지 않습니다.
labeled_statement
: identifier ':' statement
;
레이블이 지정된 문은 식별자가 지정한 이름으로 레이블을 선언합니다. 레이블의 범위는 중첩된 블록을 포함하여 레이블이 선언되는 전체 블록입니다. 이름이 같은 두 레이블에 겹치는 범위가 있는 경우 컴파일 시간 오류입니다.
레이블 범위 내의 문(§13.10.4)에서 goto
레이블을 참조할 수 있습니다.
참고: 즉
goto
, 문은 블록 내 및 블록 외부로 제어를 전송할 수 있지만 블록으로 전송할 수는 없습니다. 끝 메모
레이블에는 자체 선언 공간이 있으며 다른 식별자를 방해하지 않습니다.
예: 예제
int F(int x) { if (x >= 0) { goto x; } x = -x; x: return x; }
가 유효하고 이름 x를 매개 변수 및 레이블로 사용합니다.
끝 예제
레이블이 지정된 문의 실행은 레이블 다음에 있는 문의 실행에 정확히 해당합니다.
일반 제어 흐름에서 제공하는 연결 가능성 외에도 문이 블록 내에 있거나 catch
끝점에 연결할 수 없는 블록이 포함된 finally
try_statement 블록 내에 try
있지 않고 레이블 goto
이 지정된 문이 try_statement 외부에 있는 경우 goto
레이블이 지정된 문에 연결할 수 있습니다.
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 내에서 각 변수는 각각 암시적으로 형식화되고 명시적으로 형식화되고 ref 지역 변수에 대한 implicitly_typed_local_variable_declarator, explicitly_typed_local_variable_declarator 또는 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?
T
nullable이 아닌 참조 형식이고, 그렇지 않으면 해당 형식입니다T
. 두 번째 대안은 초기 값 ref
이 variable_reference ref 변수를 선언합니다. 해당 형식은 ref T?
T
nullable이 아닌 참조 형식이고, 그렇지 않으면 해당 형식입니다 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_kind ref readonly
선언되는 식별자는 읽기 전용으로 처리되는 변수에 대한 참조입니다. 그렇지 않은 경우 ref_kind ref
선언되는 식별자는 쓰기 가능한 변수에 대한 참조입니다.
method_modifier async
선언된 메서드 내에서 또는 반복기(§15.14) 내에서 ref 지역 변수 또는 형식의 ref struct
변수를 선언하는 것은 컴파일 시간 오류입니다.
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 ';'
;
문법 참고: null_conditional_invocation_expression 및 식 대안을 모두 적용할 수 있는 경우 local_function_body 인식할 때 전자를 선택해야 합니다. (§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 (§15.15) 한async
정자와 한정 unsafe
자(§23.1) 한정자를 포함할 수 있습니다. 선언에 한정자가 포함된 async
경우 반환 형식은 형식(«TaskType»
§15.15.1)이어야 void
합니다. 선언에 한정자가 포함된 static
경우 함수는 정적 로컬 함수이고, 그렇지 않으면 비정적 로컬 함수입니다. type_parameter_list 또는 parameter_list 특성을 포함하는 것은 컴파일 시간 오류입니다. 로컬 함수가 안전하지 않은 컨텍스트(§23.2)에서 선언된 경우 로컬 함수 선언에 한정자가 포함되지 않더라도 로컬 함수에 안전하지 않은 코드가 unsafe
포함될 수 있습니다.
로컬 함수는 블록 범위에서 선언됩니다. 비정적 로컬 함수는 바깥쪽 범위에서 변수를 캡처할 수 있지만 정적 로컬 함수는 그렇지 않습니다(따라서 바깥쪽 지역 함수, 매개 변수, 비정적 로컬 함수 또는 this
)에 액세스할 수 없습니다. 캡처된 변수가 비정적 로컬 함수의 본문에서 읽지만 함수를 호출하기 전에 확실히 할당되지 않은 경우 컴파일 시간 오류입니다. 컴파일러는 반환 시 확실히 할당된 변수를 결정해야 합니다(§9.4.4.33).
형식이 this
구조체 형식인 경우 로컬 함수의 본문에 액세스 this
하는 것은 컴파일 시간 오류입니다. 이는 액세스가 명시적(예: this.x
) 또는 암시적( x
구조체의 인스턴스 멤버인 경우 x
)이든 마찬가지입니다. 이 규칙은 이러한 액세스만 금지하며 멤버 조회로 인해 구조체의 멤버가 생성되는지 여부에는 영향을 주지 않습니다.
로컬 함수의 본문에 대상이 로컬 함수의 본문 밖에 있는 문, break
문 또는 continue
문을 포함하는 goto
것은 컴파일 시간 오류입니다.
참고: 위의 규칙은
this
§12.19.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()
허용됩니다.
13.7 식 문
expression_statement 지정된 식을 평가합니다. 식에 의해 계산된 값(있는 경우)은 삭제됩니다.
expression_statement
: statement_expression ';'
;
statement_expression
: null_conditional_invocation_expression
| invocation_expression
| object_creation_expression
| assignment
| post_increment_expression
| post_decrement_expression
| pre_increment_expression
| pre_decrement_expression
| await_expression
;
모든 식이 문으로 허용되는 것은 아닙니다.
참고: 특히 값(삭제됨)을 계산하는 식과
x == 1
같은x + y
식은 문으로 허용되지 않습니다. 끝 메모
expression_statement 실행하면 포함된 식이 평가된 다음 expression_statement 끝점으로 제어가 전송됩니다. 해당 expression_statement 연결할 수 있는 경우 expression_statement 끝점에 연결할 수 있습니다.
13.8 Selection 문
13.8.1 일반
선택 문은 일부 식의 값에 따라 실행할 수 있는 여러 문 중 하나를 선택합니다.
selection_statement
: if_statement
| switch_statement
;
13.8.2 If 문
이 문은 if
부울 식의 값에 따라 실행할 문을 선택합니다.
if_statement
: 'if' '(' boolean_expression ')' embedded_statement
| 'if' '(' boolean_expression ')' embedded_statement
'else' embedded_statement
;
부품 else
은 구문에서 허용되는 사전적으로 가장 가까운 앞에 if
연결됩니다.
예: 따라서
if
양식의 문입니다.if (x) if (y) F(); else G();
위의 식은 아래의 식과 동일합니다.
if (x) { if (y) { F(); } else { G(); } }
끝 예제
if
문은 다음과 같이 실행됩니다.
- boolean_expression(§12.24)가 평가됩니다.
- 부울 식이 생성
true
되면 컨트롤이 첫 번째 포함된 문으로 전송됩니다. 컨트롤이 해당 문의 끝점에 도달하면 제어가 문의 끝점으로if
전송됩니다. - 부울 식이 생성
false
되고else
부품이 있으면 컨트롤이 두 번째 포함된 문으로 전송됩니다. 컨트롤이 해당 문의 끝점에 도달하면 제어가 문의 끝점으로if
전송됩니다. - 부울 식이 생성
false
되고 부품이 없으면else
제어가 문의 끝점으로if
전송됩니다.
문에 if
연결할 수 있고 부울 식에 상수 값false
이 없는 경우 if
문의 첫 번째 포함된 문에 연결할 수 있습니다.
문의 두 번째 포함된 문 if
(있는 경우)은 문에 if
연결할 수 있고 부울 식에 상수 값 true
이 없는 경우 연결할 수 있습니다.
포함된 문 중 하나 이상의 끝점에 if
연결할 수 있는 경우 문의 끝점에 연결할 수 있습니다. 또한 문에 연결할 수 있고 부울 식에 상수 값true
이 없는 경우 if
파트가 없는 else
문의 끝점에 if
연결할 수 있습니다.
13.8.3 switch 문
이 문은 switch
switch 식의 값에 해당하는 연결된 스위치 레이블이 있는 문 목록을 실행하도록 선택합니다.
switch_statement
: 'switch' '(' expression ')' switch_block
;
switch_block
: '{' switch_section* '}'
;
switch_section
: switch_label+ statement_list
;
switch_label
: 'case' pattern case_guard? ':'
| 'default' ':'
;
case_guard
: 'when' expression
;
switch_statement 키워드switch
와 괄호가 있는 식(switch 식이라고 함) 뒤에 switch_block 구성됩니다. switch_block 중괄호로 묶인 0개 이상의 switch_section구성됩니다. 각 switch_section 하나 이상의 switch_label 뒤에 statement_list(§13.3.2)로 구성됩니다. 포함된 각 switch_label 스위치 식의 값이 테스트되는 연결된 패턴(§11)을 가집니다. case
case_guard 있는 경우 해당 식은 형식으로 bool
암시적으로 변환할 수 있어야 하며 해당 식은 충족된 것으로 간주될 수 있는 추가 조건으로 평가됩니다.
문의 제어 형식 switch
은 switch 식에 의해 설정됩니다.
- switch 식의 형식이
sbyte
, ,byte
,short
,ushort
,int
,uint
,long
,string
ulong
char
bool
또는 enum_type 또는 이러한 형식 중 하나에 해당하는 nullable 값 형식인 경우 해당 명령문의switch
제어 형식입니다. - 그렇지 않은 경우 스위치 식의 형식에서 다음 가능한 제어 형식 중 하나로 정확히 하나의 사용자 정의 암시적 변환이 있는 경우 ,
sbyte
,byte
,short
,long
uint
ushort
ulong
int
,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 일부이므로 "대체 안 함" 규칙을 위반하지 않습니다.끝 예제
참고: "대체 안 함" 규칙은 문이 실수로 생략될 때
break
C 및 C++에서 발생하는 일반적인 버그 클래스를 방지합니다. 예를 들어 위의 문의 섹션은switch
문의 동작에 영향을 주지 않고 되돌릴 수 있습니다.switch (i) { default: CaseAny(); break; case 1: CaseZeroOrOne(); goto default; case 0: CaseZero(); goto case 1; }
끝 메모
참고: switch 섹션의 문 목록은 일반적으로 ,
goto case
또는goto default
문으로break
끝나지만 문 목록의 끝점을 렌더링하는 모든 구문에 연결할 수 없습니다. 예를 들어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 식 문자열이 레이블 상수와 정확히 일치하는 경우에만 지정된 스위치 섹션을case
실행합니다. 끝 참고 문의 제어 형식switch
이string
거나 null 허용 값 형식인 경우 값null
은 레이블 상수로case
허용됩니다.
switch_block statement_list 선언문(§13.6)을 포함할 수 있습니다. 스위치 블록에 선언된 지역 변수 또는 상수의 범위는 스위치 블록입니다.
다음 중 하나 이상이 true인 경우 스위치 레이블에 연결할 수 있습니다.
- switch 식은 상수 값이며
- 레이블은
case
해당 패턴 이 해당 값과 일치 하고(§11.2.1) 레이블의 가드가 false 값이 있는 상수 식이 없거나 없는 경우 - 레이블이며
default
스위치 섹션에는 패턴이 해당 값과 일치하고 가드가 없거나 값이 true인 상수 식이 있는 사례 레이블이 포함되어 있지 않습니다.
- 레이블은
- switch 식은 상수 값이 아니며
- 스위치 레이블은 연결 가능 또는
goto default
문으로 참조됩니다goto case
.
문에 연결할 수 있고 스위치 섹션에 연결 가능한 스위치 레이블이 포함된 경우 switch
지정된 스위치 섹션의 문 목록에 연결할 수 있습니다.
switch 문에 switch
연결할 수 있고 다음 중 하나 이상이 true이면 문의 끝점에 도달할 수 있습니다.
- 문에는
switch
문을 종료하는 연결 가능한break
문이 포함되어 있습니다switch
. - 레이블이 없고
default
- switch 식은 비 상수 값이며, switch 문에 가드가 없거나 값이 true인 가드가 없는 경우 사이에 나타나는 패턴 집합은 스위치 제어 형식에 대해 완전하지 않습니다(§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
포함된 문 내에서 문(§13.10.2)을 사용하여 제어를 문의 끝점으로 while
전송할 수 있으며continue
(따라서 포함된 문의 반복 종료) 문(§13.10.3)을 사용하여 포함된 문의 끝점으로 제어를 전송할 수 있습니다(따라서 문의 또 다른 반복 while
수행break
).
문에 while
연결할 수 있고 부울 식에 상수 값false
이 없는 경우 while
문의 포함된 문에 연결할 수 있습니다.
다음 중 하나 이상이 true인 경우 문의 끝점에 while
연결할 수 있습니다.
- 문에는
while
문을 종료하는 연결 가능한break
문이 포함되어 있습니다while
. - 문에
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
포함된 문 내에서 문(§13.10.2)을 사용하여 제어를 문의 끝점으로 do
전송할 수 있으며continue
(따라서 포함된 문의 반복 종료) 문(§13.10.3)을 사용하여 포함된 문의 끝점으로 제어를 전송할 수 있습니다(따라서 문의 또 다른 반복 do
수행break
).
문에 연결할 수 있는 do
경우 포함된 문 문에 do
연결할 수 있습니다.
다음 중 하나 이상이 true인 경우 문의 끝점에 do
연결할 수 있습니다.
- 문에는
do
문을 종료하는 연결 가능한break
문이 포함되어 있습니다do
. - 포함된 문의 끝점에 연결할 수 있으며 부울 식에 상수 값
true
이 없습니다.
13.9.4 For 문
이 문은 for
초기화 식의 시퀀스를 평가한 다음 조건이 true인 동안 포함된 문을 반복적으로 실행하고 반복 식의 시퀀스를 평가합니다.
for_statement
: 'for' '(' for_initializer? ';' for_condition? ';' for_iterator? ')'
embedded_statement
;
for_initializer
: local_variable_declaration
| statement_expression_list
;
for_condition
: boolean_expression
;
for_iterator
: statement_expression_list
;
statement_expression_list
: statement_expression (',' statement_expression)*
;
for_initializer 있는 경우 local_variable_declaration(§13.6.2) 또는 쉼표로 구분된 statement_expression목록(§13.7)으로 구성됩니다. for_initializer 선언된 지역 변수의 범위는 for_initializer, for_condition, for_iterator 및 embedded_statement.
for_condition 있는 경우 boolean_expression(§12.24)여야 합니다.
for_iterator 있는 경우 쉼표로 구분된 statement_expression(§13.7) 목록으로 구성됩니다.
for
문은 다음과 같이 실행됩니다.
- for_initializer 있으면 변수 이니셜라이저 또는 문 식이 작성된 순서대로 실행됩니다. 이 단계는 한 번만 수행됩니다.
- for_condition 있으면 평가됩니다.
- for_condition 없거나 평가에서 생성
true
되는 경우 컨트롤이 포함된 문으로 전송됩니다. 컨트롤이 포함된 문의 끝점(문 실행continue
에서 가능)에 도달하면 for_iterator 식(있는 경우)이 순서대로 평가된 다음 위 단계의 for_condition 계산부터 시작하여 다른 반복이 수행됩니다. - for_condition 있고 계산이 생성
false
되면 제어가 문의 끝점으로for
전송됩니다.
명령문 break
의 for
포함된 문 내에서 문(§13.10.2)을 사용하여 제어를 문의 끝점으로 for
전송할 수 있으며continue
(따라서 포함된 문의 반복 종료) 문(§13.10.3)을 사용하여 임베디드 문의 끝점으로 제어를 전송할 수 있습니다(따라서 for_iterator 실행하고 문의 또 다른 반복을 수행합니다.for
for_condition)부터 시작합니다.
다음 중 하나가 true인 for
경우 명령문의 포함된 문에 연결할 수 있습니다.
- 문에
for
연결할 수 있으며 for_condition 없습니다. - 문에
for
연결할 수 있고 for_condition 있으며 상수 값false
이 없습니다.
다음 중 하나 이상이 true인 경우 문의 끝점에 for
연결할 수 있습니다.
- 문에는
for
문을 종료하는 연결 가능한break
문이 포함되어 있습니다for
. - 문에
for
연결할 수 있고 for_condition 있으며 상수 값true
이 없습니다.
13.9.5 foreach 문
이 문은 foreach
컬렉션의 요소를 열거하고 컬렉션의 각 요소에 대해 포함된 문을 실행합니다.
foreach_statement
: 'foreach' '(' ref_kind? local_variable_type identifier 'in'
expression ')' embedded_statement
;
foreach 문의 local_variable_type 및 식별자는 문의 반복 변수를 선언합니다. var
식별자가 local_variable_type 지정되고 명명된 var
형식이 범위에 없는 경우 반복 변수는 암시적으로 형식화된 반복 변수라고 하며 해당 형식은 아래에 지정된 대로 문의 요소 형식 foreach
으로 간주됩니다.
foreach_statement 둘 다 포함하거나 포함하지 않는 ref
readonly
경우 반복 변수는 읽기 전용으로 처리되는 변수를 나타냅니다. 그렇지 않으면 foreach_statement ref
없는 readonly
경우 반복 변수는 쓰기 가능한 변수를 나타냅니다.
반복 변수는 포함된 문을 통해 확장되는 범위가 있는 지역 변수에 해당합니다. 문을 실행하는 foreach
동안 반복 변수는 현재 반복이 수행되고 있는 컬렉션 요소를 나타냅니다. 반복 변수가 읽기 전용 변수를 나타내는 경우 포함된 문이 할당 또는 연산자를 통해 수정하거나 참조 또는 ++
--
출력 매개 변수로 전달하려고 하면 컴파일 시간 오류가 발생합니다.
다음에서는 간결성을 IEnumerable
IEnumerator
IEnumerable<T>
위해 네임스페이스의 System.Collections
해당 형식을 IEnumerator<T>
참조하고 .System.Collections.Generic
문의 컴파일 시간 처리는 먼저 식의 foreach
컬렉션 형식, 열거자 형식 및 반복 형식을 결정합니다. 이 결정은 다음과 같이 진행됩니다.
- 식 형식이
X
배열 형식인 경우 X에서 인터페이스로의 암시적 참조 변환이 있습니다(이 인터페이스를 구현하기IEnumerable
때문에System.Array
). 컬렉션 형식은 인터페이스이고IEnumerable
열거자 형식은IEnumerator
인터페이스이고 반복 형식은 배열 형식의 요소 형식X
입니다. - 식 형식
X
이면 식에서 인터페이스로IEnumerable
의 암시적 변환이 있습니다(§10.2.10).dynamic
컬렉션 형식은IEnumerable
인터페이스이고 열거자 형식은 인터페이스입니다IEnumerator
.var
식별자가 local_variable_type 지정되면 반복 형식이고dynamic
, 그렇지 않으면 다음과 같습니다object
. - 그렇지 않으면 형식
X
에 적절한GetEnumerator
메서드가 있는지 확인합니다.- 식별자와
GetEnumerator
형식 인수가 없는 형식X
에서 멤버 조회를 수행합니다. 멤버 조회가 일치 항목을 생성하지 않거나 모호성을 생성하거나 메서드 그룹이 아닌 일치 항목을 생성하는 경우 아래 설명된 대로 열거 가능한 인터페이스를 확인합니다. 멤버 조회에서 메서드 그룹을 제외한 모든 항목을 생성하거나 일치하지 않는 경우 경고를 발생시키는 것이 좋습니다. - 결과 메서드 그룹 및 빈 인수 목록을 사용하여 오버로드 확인을 수행합니다. 오버로드 확인으로 인해 적용 가능한 메서드가 없거나 모호성이 발생하거나 단일 최상의 메서드가 생성되지만 해당 메서드가 정적이거나 공용이 아닌 경우 아래 설명된 대로 열거 가능한 인터페이스를 확인합니다. 오버로드 확인에서 명확한 퍼블릭 인스턴스 메서드를 제외한 모든 항목이 생성되거나 적용 가능한 메서드가 없는 경우 경고를 발생시키는 것이 좋습니다.
- 메서드의
GetEnumerator
반환 형식E
이 클래스, 구조체 또는 인터페이스 형식이 아닌 경우 오류가 생성되고 추가 단계가 수행되지 않습니다. - 멤버 조회는 식별자와
Current
형식 인수 없이 수행E
됩니다. 멤버 조회에서 일치하는 항목이 생성되지 않거나, 결과가 오류이거나, 읽기를 허용하는 공용 인스턴스 속성을 제외한 모든 항목이 결과인 경우 오류가 생성되고 추가 단계가 수행되지 않습니다. - 멤버 조회는 식별자와
MoveNext
형식 인수 없이 수행E
됩니다. 멤버 조회에서 일치하는 항목이 생성되지 않거나, 결과가 오류이거나, 결과가 메서드 그룹을 제외한 모든 항목인 경우 오류가 생성되고 추가 단계가 수행되지 않습니다. - 오버로드 확인은 빈 인수 목록을 사용하여 메서드 그룹에서 수행됩니다. 오버로드 확인으로 인해 적용 가능한 메서드가 없거나 모호성이 발생하거나 단일 최상의 메서드가 발생하지만 해당 메서드가 정적이거나 public이 아니거나 반환 형식이 아닌 경우 오류가 생성되고 추가 단계가 수행되지 않습니다
bool
. - 컬렉션 형식은
X
열거자 형식이고E
반복 형식은 속성의Current
형식입니다. 속성에는Current
한정자가 포함될ref
수 있습니다. 이 경우 반환되는 식은 선택적으로 읽기 전용인 variable_reference (§9.5)입니다.
- 식별자와
- 그렇지 않으면 열거 가능한 인터페이스를 확인합니다.
- 암시적 변환이 있는 모든 형식
Tᵢ
중에서X
IEnumerable<Tᵢ>
암시적 변환이 없는 고유 형식T
dynamic
T
이 있고 다른Tᵢ
모든 형식에 대해 암시적 변환IEnumerable<T>
IEnumerable<Tᵢ>
이 있는 경우 컬렉션 형식은 인터페이스IEnumerable<T>
이고 열거자 형식은 인터페이스IEnumerator<T>
이며 반복 형식은 다음과 같T
습니다. - 그렇지 않으면 이러한 형식
T
이 두 개 이상 있는 경우 오류가 생성되고 추가 단계가 수행되지 않습니다. - 그렇지 않으면 인터페이스로
X
System.Collections.IEnumerable
의 암시적 변환이 있는 경우 컬렉션 형식은 이 인터페이스이고 열거자 형식은 인터페이스System.Collections.IEnumerator
이고 반복 형식은 다음과 같습니다object
. - 그렇지 않으면 오류가 생성되고 추가 단계가 수행되지 않습니다.
- 암시적 변환이 있는 모든 형식
위의 단계는 성공적이면 컬렉션 형식, 열거자 형식 C
및 반복 형식 ref T
E
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
(문의 local_variable_typeforeach
) V
없는 경우 오류가 발생하며 추가 단계가 수행되지 않습니다.
반복 변수가 참조 변수(§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)해서는 안 됩니다. ID 변환(§10.2.2)이 (반복 유형)에서 T
(문의 local_variable_typeforeach
)V
로 변환되지 않으면 오류가 발생하며 추가 단계가 수행되지 않습니다.
foreach
폼 foreach (ref readonly V v in x) «embedded_statement»
의 문은 비슷한 형식이지만 참조 변수 v
는 ref readonly
포함된 문에 있으므로 ref-reassigned 또는 다시 할당할 수 없습니다.
참고: 값
null
System.NullReferenceException
이 있는 경우x
런타임에 throw됩니다. 끝 메모
구현은 지정된 foreach_statement 다르게 구현할 수 있습니다( 예: 성능상의 이유로 동작이 위의 확장과 일치하는 경우).
루프 내부 while
배치 v
는 embedded_statement 발생하는 익명 함수에 의해 캡처되는 방식(§12.19.6.2)에 중요합니다.
예제:
int[] values = { 7, 9, 13 }; Action f = null; foreach (var value in values) { if (f == null) { f = () => Console.WriteLine("First value: " + value); } } f();
확장된 형식에서 루프 외부에서
while
선언된 경우v
모든 반복 간에 공유되고 루프 이후for
의 값은 최종 값이 됩니다. 이 값13
은 호출f
이 인쇄되는 값입니다. 대신 각 반복에 고유한 변수v
가 있으므로 첫 번째 반복에서 캡처한f
변수는 계속 값을 저장합니다. 이 값7
은 인쇄됩니다. (이전 버전의 C#은 루프 외부에서while
선언되었습니다v
.)끝 예제
블록의 finally
본문은 다음 단계에 따라 생성됩니다.
인터페이스로
E
의 암시적 변환이System.IDisposable
있는 경우nullable이 아닌 값 형식
finally
인 경우E
절이 다음과 같은 의미 체계로 확장됩니다.finally { ((System.IDisposable)e).Dispose(); }
그렇지 않으면
finally
절이 다음과 같은 의미 체계로 확장됩니다.finally { System.IDisposable d = e as System.IDisposable; if (d != null) { d.Dispose(); } }
값 형식이거나 값 형식으로 인스턴스화된
e
System.IDisposable
형식 매개 변수인 경우E
변환으로 인해 boxing이 발생하지 않습니다.
그렇지 않으면 봉인된 형식
finally
인 경우E
절이 빈 블록으로 확장됩니다.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.10 Jump 문
13.10.1 일반
점프 문은 무조건 제어를 전송합니다.
jump_statement
: break_statement
| continue_statement
| goto_statement
| return_statement
| throw_statement
;
jump 문이 컨트롤을 전송하는 위치를 jump 문의 대상이라고 합니다.
점프 문이 블록 내에서 발생하고 해당 jump 문의 대상이 해당 블록 외부에 있는 경우 jump 문은 블록을 종료한다고 합니다. jump 문은 블록 외부로 제어를 전송할 수 있지만 제어를 블록으로 전송할 수 없습니다.
jump 문의 실행은 중간 try
문이 있기 때문에 복잡합니다. 이러한 try
명령문이 없는 경우 jump 문은 점프 문에서 해당 대상으로 제어를 무조건 전송합니다. 이러한 중간 try
문이 있는 경우 실행이 더 복잡합니다. jump 문이 연결된 finally
블록과 함께 하나 이상의 try
블록을 종료하는 경우 컨트롤은 처음에 가장 try
안쪽 문의 블록으로 전송됩니다finally
. 컨트롤이 블록의 finally
끝점에 도달하면 다음 바깥쪽 try
문의 블록으로 컨트롤이 전송 finally
됩니다. 이 프로세스는 모든 중간 try
문의 블록이 실행될 때까지 finally
반복됩니다.
예제: 다음 코드에서
class Test { static void Main() { while (true) { try { try { Console.WriteLine("Before break"); break; } finally { Console.WriteLine("Innermost finally block"); } } finally { Console.WriteLine("Outermost finally block"); } } Console.WriteLine("After break"); } }
제어가
finally
jump 문의 대상으로 전송되기 전에 두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
, while
, do
for
또는 foreach
문으로 switch
묶이지 않으면 컴파일 시간 오류가 발생합니다.
여러 switch
문, while
, 또는 for
do
foreach
문이 서로 break
중첩되는 경우 문은 가장 안쪽 문에만 적용됩니다. 여러 중첩 수준에서 goto
제어를 전송하기 위해 문(§13.10.4)을 사용해야 합니다.
문은 break
블록을 종료 finally
할 수 없습니다(§13.11). break
문이 블록 내에서 finally
발생하면 문의 대상이 break
동일한 finally
블록 내에 있어야 합니다. 그렇지 않으면 컴파일 시간 오류가 발생합니다.
break
문은 다음과 같이 실행됩니다.
break
문이 연결된finally
블록과 함께 하나 이상의try
블록을 종료하는 경우 컨트롤은 처음에 가장try
안쪽 문의 블록으로 전송됩니다finally
. 컨트롤이 블록의finally
끝점에 도달하면 다음 바깥쪽try
문의 블록으로 컨트롤이 전송finally
됩니다. 이 프로세스는 모든 중간try
문의 블록이 실행될 때까지finally
반복됩니다.- 컨트롤이 문의 대상으로
break
전송됩니다.
문은 break
무조건 제어를 다른 곳으로 전송하기 때문에 문의 끝점에 break
도달할 수 없습니다.
13.10.3 continue 문
문은 continue
가장 가까운 바깥쪽, do
또는 for
foreach
문의 새 반복을 while
시작합니다.
continue_statement
: 'continue' ';'
;
문의 대상 continue
은 가장 가까운 바깥쪽while
, do
또는 for
foreach
문에 포함된 문의 끝점입니다. 문이 , continue
do
, for
또는 foreach
문으로 while
묶이지 않으면 컴파일 시간 오류가 발생합니다.
여러 while
, do
, for
또는 foreach
문이 서로 continue
중첩된 경우 문은 가장 안쪽 문에만 적용됩니다. 여러 중첩 수준에서 goto
제어를 전송하기 위해 문(§13.10.4)을 사용해야 합니다.
문은 continue
블록을 종료 finally
할 수 없습니다(§13.11). continue
문이 블록 내에서 finally
발생하면 문의 대상이 continue
동일한 finally
블록 내에 있어야 합니다. 그렇지 않으면 컴파일 시간 오류가 발생합니다.
continue
문은 다음과 같이 실행됩니다.
continue
문이 연결된finally
블록과 함께 하나 이상의try
블록을 종료하는 경우 컨트롤은 처음에 가장try
안쪽 문의 블록으로 전송됩니다finally
. 컨트롤이 블록의finally
끝점에 도달하면 다음 바깥쪽try
문의 블록으로 컨트롤이 전송finally
됩니다. 이 프로세스는 모든 중간try
문의 블록이 실행될 때까지finally
반복됩니다.- 컨트롤이 문의 대상으로
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
상수 패턴과 가드가 없는 레이블을 포함하는 case
즉시 바깥쪽 switch
문(§13.8.3)의 문 목록입니다. goto case
문이 문으로 switch
묶이지 않은 경우, 가장 가까운 바깥쪽 switch
문에 이러한 case
문이 포함되어 있지 않거나 constant_expression 가장 switch
가까운 바깥쪽 문의 제어 형식으로 암시적으로 변환할 수 없는 경우 컴파일 시간 오류가 발생합니다.
문의 대상 goto default
은 레이블을 포함하는 즉시 바깥쪽 switch
문(§13.8.3)의 문 목록입니다 default
. goto default
문이 문으로 switch
묶이지 않거나 가장 가까운 바깥쪽 switch
문에 레이블이 없 default
으면 컴파일 시간 오류가 발생합니다.
문은 goto
블록을 종료 finally
할 수 없습니다(§13.11). goto
문이 블록 내에서 finally
발생하면 문의 대상이 goto
동일한 finally
블록 내에 있거나 컴파일 시간 오류가 발생합니다.
goto
문은 다음과 같이 실행됩니다.
goto
문이 연결된finally
블록과 함께 하나 이상의try
블록을 종료하는 경우 컨트롤은 처음에 가장try
안쪽 문의 블록으로 전송됩니다finally
. 컨트롤이 블록의finally
끝점에 도달하면 다음 바깥쪽try
문의 블록으로 컨트롤이 전송finally
됩니다. 이 프로세스는 모든 중간try
문의 블록이 실행될 때까지finally
반복됩니다.- 컨트롤이 문의 대상으로
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_statement return-by-ref라고 하며, 식만 포함하는 return_statement 반환 값이라고 부릅니다.
반환 값 또는 return-by-ref(§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_modifier async
선언된 메서드의 return-by-ref를 사용하는 것은 컴파일 시간 오류입니다.
함수 멤버는 값 별 반환 메서드(§15.6.11), 속성 또는 인덱서의 값별 반환 가져오기 접근자 또는 사용자 정의 연산자가 있는 메서드인 경우 값을 계산하는 것으로 합니다. return-no-value인 함수 멤버는 값을 계산하지 않으며 유효 반환 형식 void
을 사용하는 메서드, 속성 및 인덱서의 접근자 설정, 이벤트, 인스턴스 생성자, 정적 생성자 및 종료자의 접근자를 추가 및 제거합니다. return-by-ref인 함수 멤버는 값을 계산하지 않습니다.
반환 값의 경우 식 형식에서 포함하는 함수 멤버의 유효 반환 형식(§15.6.11)으로 암시적 변환(§10.2)이 있어야 합니다. return-by-ref의 경우 식 형식과 포함하는 함수 멤버의 유효 반환 형식 사이에 ID 변환(§10.2.2)이 있어야 합니다.
return
무명 함수 식(§12.19)의 본문에도 문을 사용하고 해당 함수에 대해 존재하는 변환을 결정하는 데 참여할 수 있습니다(§10.7.1).
문이 블록에 표시되는 finally
컴파일 시간 오류 return
입니다(§13.11).
return
문은 다음과 같이 실행됩니다.
- 반환 값의 경우 식 이 계산되고 해당 값이 암시적 변환을 통해 포함하는 함수의 유효 반환 형식으로 변환됩니다. 변환 결과는 함수에 의해 생성된 결과 값이 됩니다. return-by-ref의 경우 식 이 평가되고 결과는 변수로 분류되어야 합니다. 바깥쪽 메서드의 return-by-ref에 포함된
readonly
경우 결과 변수는 읽기 전용입니다. return
문이 하나 이상try
또는catch
연결된finally
블록으로 묶인 경우 컨트롤은 처음에 가장try
안쪽 문의 블록으로 전송됩니다finally
. 컨트롤이 블록의finally
끝점에 도달하면 다음 바깥쪽try
문의 블록으로 컨트롤이 전송finally
됩니다. 이 프로세스는 모든 바깥쪽try
문의 블록이 실행될 때까지finally
반복됩니다.- 포함하는 함수가 비동기 함수가 아닌 경우 컨트롤은 결과 값과 함께 포함하는 함수의 호출자에게 반환됩니다(있는 경우).
- 포함하는 함수가 비동기 함수인 경우 컨트롤이 현재 호출자에게 반환되고 결과 값(있는 경우)은 (§15.15.3)에 설명된 대로 반환 작업에 기록됩니다.
문은 return
무조건 제어를 다른 곳으로 전송하기 때문에 문의 끝점에 return
도달할 수 없습니다.
13.10.6 throw 문
문은 throw
예외를 throw합니다.
throw_statement
: 'throw' expression? ';'
;
throw
식이 있는 문은 식을 계산하여 생성된 예외를 throw합니다. 식은 암시적으로 변환할 수 System.Exception
있어야 하며 식을 계산한 결과는 throw되기 전에 변환 System.Exception
됩니다. 변환 null
결과가 있으면 대신 throw System.NullReferenceException
됩니다.
식이 없는 문은 throw
블록에서 catch
만 사용할 수 있습니다. 이 경우 해당 문은 해당 블록에서 현재 처리 catch
중인 예외를 다시 throw합니다.
문은 throw
무조건 제어를 다른 곳으로 전송하기 때문에 문의 끝점에 throw
도달할 수 없습니다.
예외가 throw되면 컨트롤이 예외를 처리할 수 있는 바깥쪽 try
문의 첫 번째 catch
절로 전송됩니다. throw되는 예외 지점에서 적절한 예외 처리기로 제어를 전송하는 지점까지 발생하는 프로세스를 예외 전파라고 합니다. 예외 전파는 예외와 일치하는 절이 발견될 때까지 catch
다음 단계를 반복적으로 평가하는 것으로 구성됩니다. 이 설명 에서 throw 지점 은 처음에는 예외가 throw되는 위치입니다. 이 동작은 (§21.4)에 지정됩니다.
현재 함수 멤버에서 throw 지점을 묶는 각
try
문이 검사됩니다. 각 문S
에 대해 가장try
안쪽 문으로 시작하고 가장try
바깥쪽 문으로 끝나는 다음 단계가 평가됩니다.- 블록
S
이try
throw 지점을 묶고 하나 이상의catch
절catch
이 있는 경우S
절은 모양 순서대로 검사되어 예외에 적합한 처리기를 찾습니다. 예외 형식T
을 지정하는 첫 번째catch
절(또는 런타임 시 예외 형식T
을 나타내는 형식 매개 변수)을 지정하여 런타임 형식이E
일치하는T
것으로 간주됩니다. 절에 예외 필터가 포함된 경우 예외 개체가 예외 변수에 할당되고 예외 필터가 평가됩니다. 절에catch
예외 필터가 포함된 경우 예외 필터가 으로 계산되는 경우 해당catch
절은 일치 항목으로true
간주됩니다. 일반catch
(§13.11) 절은 모든 예외 형식에 대한 일치 항목으로 간주됩니다. 일치하는catch
절이 있으면 컨트롤을 해당 절의 블록으로 전송하여 예외 전파가catch
완료됩니다. - 그렇지 않으면
try
블록 또는catch
블록이S
throw 지점을 묶고 블록이 있는finally
경우S
컨트롤이 블록으로finally
전송됩니다. 블록이finally
다른 예외를 throw하면 현재 예외 처리가 종료됩니다. 그렇지 않으면 컨트롤이 블록의finally
끝점에 도달하면 현재 예외 처리가 계속됩니다.
- 블록
예외 처리기가 현재 함수 호출에 없는 경우 함수 호출이 종료되고 다음 중 하나가 발생합니다.
현재 함수가 비동기인 경우 함수 멤버가 호출된 문에 해당하는 throw 지점이 있는 함수 호출자에 대해 위의 단계가 반복됩니다.
현재 함수가 비동기 및 작업 반환인 경우 예외는 반환 태스크에 기록됩니다. 이 작업은 §15.15.3에 설명된 대로 오류 또는 취소된 상태로 전환됩니다.
현재 함수가 비동기 및
void
-returning이면 §15.15.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
있습니다. 끝 메모
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
는 예외를 catch하고, 일부 진단 정보를 콘솔에 쓰고, 예외 변수를 변경하고, 예외를 다시 throw합니다. 다시 throw되는 예외는 원래 예외이므로 생성된 출력은 다음과 같습니다.Exception in F: G Exception in Main: G
현재 예외를 다시 throw하는 대신 첫 번째
catch
블록이 throwe
된 경우 생성된 출력은 다음과 같습니다.Exception in F: G Exception in Main: F
끝 예제
컨트롤을 블록 밖으로 전송하는 문 또는 goto
문에 대한 continue
break
컴파일 시간 오류입니다finally
. 블록에서 break
finally
, 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
문을 떠날 때 항상 실행됩니다. 이는 컨트롤 전송이 일반적인 실행의 결과로, , continue
또는 문을 실행한 break
결과로 발생하거나 return
문에서 try
예외를 전파한 결과로 발생하는지 여부에 관계없이 적용goto
됩니다. 예외가 전파되지 않고 컨트롤이 finally
블록의 끝점에 도달하면 제어가 문의 끝점으로 try
전송됩니다.
블록을 실행하는 finally
동안 예외가 throw되고 동일한 finally
블록 내에서 catch되지 않으면 예외가 다음 바깥쪽 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
바깥쪽 일치catch
절Method
로 전송되기 전에 절이 실행됩니다. 결과 출력은 다음과 같습니다.Filter Finally Catch
끝 예제
try
문에 try
연결할 수 있는 경우 문 블록에 try
연결할 수 있습니다.
catch
문에 try
연결할 수 있는 경우 문 블록에 try
연결할 수 있습니다.
finally
문에 try
연결할 수 있는 경우 문 블록에 try
연결할 수 있습니다.
다음 두 가지 모두 true인 경우 문의 끝점에 try
연결할 수 있습니다.
- 블록의
try
끝점에 연결할 수 있거나 하나catch
이상의 블록의 끝점에 연결할 수 있습니다. - 블록이
finally
있으면 블록의finally
끝점에 연결할 수 있습니다.
13.12 확인된 문과 선택되지 않은 문
checked
및 unchecked
문은 정수 형식 산술 연산 및 변환에 대한 오버플로 검사 컨텍스트를 제어하는 데 사용됩니다.
checked_statement
: 'checked' block
;
unchecked_statement
: 'unchecked' block
;
이 문은 checked
블록의 모든 식이 확인된 컨텍스트에서 계산되도록 하고unchecked
, 이 문을 사용하면 블록의 모든 식이 선택되지 않은 컨텍스트에서 계산됩니다.
및 문은 checked
식 대신 블록에서 작동한다는 점을 제외하고 연산자(§12.8.20)와 unchecked
unchecked
정확하게 동일합니다checked
.
13.13 lock 문
이 문은 lock
지정된 개체에 대한 상호 배제 잠금을 가져오고 문을 실행한 다음 잠금을 해제합니다.
lock_statement
: 'lock' '(' expression ')' embedded_statement
;
문 식 lock
은 참조로 알려진 형식의 값을 나타냅니다. 문 식에 대해 lock
암시적 boxing 변환(§10.2.9)이 수행되지 않으므로 식이 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
하는 클래스 또는 구조체입니다Dispose
. 리소스를 사용하는 코드는 리소스가 더 이상 필요하지 않음을 나타내기 위해 호출 Dispose
할 수 있습니다.
resource_acquisition 형식이 local_variable_declaration 경우 local_variable_declaration 형식은 암시적으로 변환System.IDisposable
할 수 있는 형식이어야 dynamic
합니다. resource_acquisition 형식이 식인 경우 이 식은 암시적으로 변환할 System.IDisposable
수 있습니다.
resource_acquisition 선언된 지역 변수는 읽기 전용이며 이니셜라이저를 포함해야 합니다. 포함된 문에서 할당 또는 연산자를 통해 이러한 지역 변수를 수정하거나, 해당 주소를 사용하거나 ++
--
, 참조 또는 출력 매개 변수로 전달하려고 하면 컴파일 시간 오류가 발생합니다.
using
문은 인수, 사용 및 폐기의 세 부분으로 변환됩니다. 리소스 사용은 절을 포함하는 finally
문에 try
암시적으로 묶입니다. 이 finally
절은 리소스를 삭제합니다. 리소스를 null
획득하면 호출 Dispose
이 이루어지지 않으며 예외가 throw되지 않습니다. 리소스가 형식 dynamic
인 경우 사용 및 폐기 전에 변환이 성공하도록 하기 위해 인수 중에 암시적 동적 변환(§10.2.10) IDisposable
을 통해 동적으로 변환됩니다.
using
양식의 문입니다.
using (ResourceType resource = «expression» ) «statement»
는 세 가지 가능한 확장 중 하나에 해당합니다. nullable이 아닌 값 형식 또는 값 형식 제약 조건(§15.2.5)이 있는 형식 매개 변수인 경우 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
자체가 .로 ResourceType
사용됩니다. 변수는 resource
포함된 문에 액세스할 수 없고 보이지 않습니다.
resource_acquisition local_variable_declaration 형식을 사용하는 경우 지정된 형식의 여러 리소스를 획득할 수 있습니다. using
양식의 문입니다.
using (ResourceType r1 = e1, r2 = e2, ..., rN = eN) «statement»
는 중첩된 using
문 시퀀스에 정확히 동일합니다.
using (ResourceType r1 = e1)
using (ResourceType r2 = e2)
...
using (ResourceType rN = eN)
«statement»
예제: 아래 예제에서는 log.txt 파일을 만들고 파일에 두 줄의 텍스트를 씁니다. 그런 다음 이 예제에서는 읽기 위해 동일한 파일을 열고 포함된 텍스트 줄을 콘솔에 복사합니다.
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
인터페이스를IDisposable
구현하므로 이 예제에서는 문을 사용하여using
쓰기 또는 읽기 작업 후에 기본 파일이 제대로 닫히도록 할 수TextReader
있습니다.끝 예제
13.15 수익률 문
이 yield
문은 반복기 블록(§13.3)에서 반복기의 열거자 개체(§15.14.5) 또는 열거 가능한 개체(§15.14.6)에 값을 생성하거나 반복의 끝을 알리는 데 사용됩니다.
yield_statement
: 'yield' 'return' expression ';'
| 'yield' 'break' ';'
;
yield
는 상황별 키워드(§6.4.4)이며, 키워드 또는 키워드 바로 앞에 return
break
사용되는 경우에만 특별한 의미를 줍니다.
다음에 설명된 대로 문이 나타날 수 있는 위치에 yield
는 몇 가지 제한 사항이 있습니다.
- method_body, operator_body 또는 accessor_body 외부에 나타나는 문(양식 중 하나)의 컴파일 시간 오류
yield
입니다. - 무명 함수 내에 문(두 폼 중 하나)이 표시되는 것은 컴파일 시간 오류
yield
입니다. - 문 절에
yield
나타나는finally
문(두 형식 중 하나)의 컴파일 시간 오류입니다try
. - 문이 catch_clauses 포함하는 문의 아무 곳에나
try
표시되는 것은 컴파일 시간 오류yield return
입니다.
예제: 다음 예제에서는 유효하고 잘못된 문 사용을
yield
보여 줍니다.delegate IEnumerable<int> D(); IEnumerator<int> GetEnumerator() { try { yield return 1; // Ok yield break; // Ok } finally { yield return 2; // Error, yield in finally yield break; // Error, yield in finally } try { yield return 3; // Error, yield return in try/catch yield break; // Ok } catch { yield return 4; // Error, yield return in try/catch yield break; // Ok } D d = delegate { yield return 5; // Error, yield in an anonymous function }; } int MyMethod() { yield return 1; // Error, wrong return type for an iterator block }
끝 예제
암시적 변환(§10.2)은 문에 있는 식의 형식에서 yield return
반복기의 수율 형식(§15.14.4)으로 존재해야 합니다.
yield return
문은 다음과 같이 실행됩니다.
- 문에 지정된 식은 계산되고 암시적으로 수율 형식으로 변환되며 열거자 개체의 속성에
Current
할당됩니다. - 반복기 블록의 실행이 일시 중단됩니다.
yield return
문이 하나 이상의try
블록 내에 있는 경우 연결된finally
블록은 현재 실행되지 않습니다. - 열거자 개체의 메서드는
MoveNext
호출자로 반환true
되며, 이는 열거자 개체가 다음 항목으로 성공적으로 진행되었음을 나타냅니다.
열거자 개체의 MoveNext
메서드에 대한 다음 호출은 마지막으로 일시 중단된 반복기 블록의 실행을 다시 시작합니다.
yield break
문은 다음과 같이 실행됩니다.
yield break
문이 하나 이상의try
블록과 연결된finally
블록으로 묶인 경우 컨트롤은 처음에 가장try
안쪽 문의 블록으로 전송됩니다finally
. 컨트롤이 블록의finally
끝점에 도달하면 다음 바깥쪽try
문의 블록으로 컨트롤이 전송finally
됩니다. 이 프로세스는 모든 바깥쪽try
문의 블록이 실행될 때까지finally
반복됩니다.- 컨트롤이 반복기 블록의 호출자에게 반환됩니다.
MoveNext
열거자 개체의 메서드 또는Dispose
메서드입니다.
문은 yield break
무조건 제어를 다른 곳으로 전송하기 때문에 문의 끝점에 yield break
도달할 수 없습니다.
ECMA C# draft specification