다음을 통해 공유


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 두 피연산 == 자가 상수이므로 문의 부울 식은 상수 식입니다. 상수 식이 컴파일 시간에 계산되어 값을 falseConsole.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)에 도달할 수 있으므로 문에 연결할 수 있습니다.
  • 문의 부울 식에 상수 값falseif 없으므로 두 번째 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_iteratorembedded_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, stringulongcharbool또는 enum_type 또는 이러한 형식 중 하나에 해당하는 nullable 값 형식인 경우 해당 명령문의 switch 제어 형식입니다.
  • 그렇지 않은 경우 스위치 식의 형식에서 다음 가능한 제어 형식 중 하나로 정확히 하나의 사용자 정의 암시적 변환이 있는 경우 , sbyte,byte, short, longuintushortulongint, charstring, 또는 해당 형식 중 하나에 해당하는 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 전송됩니다.

참고: 런타임 시 패턴이 일치하는 순서는 정의되지 않습니다. 컴파일러는 순서가 맞지 않는 패턴을 일치시키고 이미 일치된 패턴의 결과를 다시 사용하여 다른 패턴의 일치 결과를 계산할 수 있습니다(필수는 아님). 그럼에도 불구하고 컴파일러는 식과 일치하고 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 실행합니다. 끝 참고 문의 제어 형식 switchstring 거나 null 허용 값 형식인 경우 값 null 은 레이블 상수로 case 허용됩니다.

switch_block statement_list 선언문(§13.6)을 포함할 수 있습니다. 스위치 블록에 선언된 지역 변수 또는 상수의 범위는 스위치 블록입니다.

다음 중 하나 이상이 true인 경우 스위치 레이블에 연결할 수 있습니다.

  • switch 식은 상수 값이며
    • 레이블은 case 해당 패턴 이 해당 값과 일치 하고(§11.2.1) 레이블의 가드가 false 값이 있는 상수 식이 없거나 없는 경우
    • 레이블이며 default 스위치 섹션에는 패턴이 해당 값과 일치하고 가드가 없거나 값이 true인 상수 식이 있는 사례 레이블이 포함되어 있지 않습니다.
  • switch 식은 상수 값이 아니며
    • 레이블은 case 가드가 없거나 값이 상수 false가 아닌 가드가 있는 레이블입니다.
    • 레이블이고 default
      • 스위치 제어 유형에 대해 가드가 없거나 값이 상수 true인 가드가 없는 switch 문의 경우 중 나타나는 패턴 집합(§11.4)입니다 .
      • 스위치 관리 형식은 nullable 형식이며, 값이 true인 상수인 가드가 없는 switch 문의 경우와 일치하는 패턴 집합이 해당 값 null과 일치하는 패턴을 포함하지 않습니다.
  • 스위치 레이블은 연결 가능 또는 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_iteratorembedded_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 전송됩니다.

명령문 breakfor 포함된 문 내에서 문(§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 동안 반복 변수는 현재 반복이 수행되고 있는 컬렉션 요소를 나타냅니다. 반복 변수가 읽기 전용 변수를 나타내는 경우 포함된 문이 할당 또는 연산자를 통해 수정하거나 참조 또는 ++ -- 출력 매개 변수로 전달하려고 하면 컴파일 시간 오류가 발생합니다.

다음에서는 간결성을 IEnumerableIEnumeratorIEnumerable<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 TE 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로 변환되지 않으면 오류가 발생하며 추가 단계가 수행되지 않습니다.

foreachforeach (ref readonly V v in x) «embedded_statement» 의 문은 비슷한 형식이지만 참조 변수 vref readonly 포함된 문에 있으므로 ref-reassigned 또는 다시 할당할 수 없습니다.

참고: 값 nullSystem.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또는 forforeach 문을 종료합니다.

break_statement
    : 'break' ';'
    ;

문의 대상 break 은 가장 가까운 바깥쪽switch, while, , dofor또는 foreach 문의 끝점입니다. 문이 , break , while, dofor또는 foreach 문으로 switch묶이지 않으면 컴파일 시간 오류가 발생합니다.

여러 switch문, while, 또는 fordoforeach 문이 서로 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또는 forforeach 문의 새 반복을 while시작합니다.

continue_statement
    : 'continue' ';'
    ;

문의 대상 continue 은 가장 가까운 바깥쪽while, do또는 forforeach 문에 포함된 문의 끝점입니다. 문이 , 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 바깥쪽 문으로 끝나는 다음 단계가 평가됩니다.

    • 블록 Stry throw 지점을 묶고 하나 이상의 catchcatch 이 있는 경우 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_filtercatch 블록을 실행하는 동안 예외 변수는 현재 처리 중인 예외를 나타냅니다. 명확한 할당 검사를 위해 예외 변수는 전체 범위에서 확실히 할당된 것으로 간주됩니다.

절에 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 블록이 throw e 된 경우 생성된 출력은 다음과 같습니다.

Exception in F: G
Exception in Main: F

끝 예제

컨트롤을 블록 밖으로 전송하는 문 또는 goto 문에 대한 continuebreak컴파일 시간 오류입니다finally. 블록에서 breakfinally , 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 바깥쪽 일치 catchMethod 로 전송되기 전에 절이 실행됩니다. 결과 출력은 다음과 같습니다.

Filter
Finally
Catch

끝 예제

try 문에 try 연결할 수 있는 경우 문 블록에 try 연결할 수 있습니다.

catch 문에 try 연결할 수 있는 경우 문 블록에 try 연결할 수 있습니다.

finally 문에 try 연결할 수 있는 경우 문 블록에 try 연결할 수 있습니다.

다음 두 가지 모두 true인 경우 문의 끝점에 try 연결할 수 있습니다.

  • 블록의 try 끝점에 연결할 수 있거나 하나 catch 이상의 블록의 끝점에 연결할 수 있습니다.
  • 블록이 finally 있으면 블록의 finally 끝점에 연결할 수 있습니다.

13.12 확인된 문과 선택되지 않은 문

checkedunchecked 문은 정수 형식 산술 연산 및 변환에 대한 오버플로 검사 컨텍스트를 제어하는 데 사용됩니다.

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 도달할 수 없습니다.