다음을 통해 공유


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 비터미널은 다른 문 내에 나타나는 문에 사용됩니다. statement 대신 embedded_statement을 사용하면 이러한 컨텍스트에서 선언문과 레이블문을 사용할 수 없습니다.

예제: 코드

void F(bool b)
{
   if (b)
      int i = 44;
}

if 문이 해당 분기에 대한 이 아닌 embedded_statement를 필요로 하기 때문에 컴파일 시간 오류가 발생합니다if. 이 코드가 허용된 경우 변수 i 가 선언되지만 사용할 수 없습니다. 그러나 i의 선언을 블록 안에 배치하면 예제가 유효해집니다.

예제 종료

13.2 끝점 및 연결 가능성

모든 문에는 끝점이 있습니다. 직관적 측면에서 명령문의 끝점은 명령문 바로 뒤에 있는 위치입니다. 복합 문(포함된 문을 포함하는 문)에 대한 실행 규칙은 컨트롤이 포함된 문의 끝점에 도달할 때 수행되는 작업을 지정합니다.

: 컨트롤이 블록에서 문의 끝점에 도달하면 컨트롤이 블록의 다음 문으로 전송됩니다. 예제 종료

실행으로 문에 도달할 수 있는 경우, 그 문을 도달할 수 있다고 합니다. 반대로 문장이 실행될 가능성이 없으면 해당 문장은 도달할 수 없는 문이라고 합니다.

예제: 다음 코드에서

void F()
{
    Console.WriteLine("reachable");
    goto Label;
    Console.WriteLine("unreachable");
  Label:
    Console.WriteLine("reachable");
}

문이 실행될 가능성이 없으므로 Console.WriteLine의 두 번째 호출은 도달할 수 없습니다.

예제 종료

throw_statement, 블록 또는 empty_statement 이외의 문에 도달할 수 없는 경우 경고가 보고됩니다. 문장이 실행되지 않더라도 오류가 아닙니다.

참고: 특정 문 또는 끝점에 연결할 수 있는지 여부를 확인하기 위해 컴파일러는 각 문에 대해 정의된 연결 가능성 규칙에 따라 흐름 분석을 수행합니다. 흐름 분석은 문의 동작을 제어하는 상수 식(§12.23)의 값을 고려하지만 상수가 아닌 식의 가능한 값은 고려되지 않습니다. 즉, 제어 흐름 분석을 위해 지정된 형식의 비 상수 식은 해당 형식의 가능한 값을 갖는 것으로 간주됩니다.

예제에서

void F()
{
    const int i = 1;
    if (i == 2)
        Console.WriteLine("unreachable");
}

부울 식이 if 문의 상수 식인 이유는 연산자 ==의 두 피연산자가 모두 상수이기 때문입니다. 상수 식이 컴파일 시간에 계산되어 값 false를 생성하여, Console.WriteLine 호출은 도달할 수 없는 것으로 간주됩니다. 그러나 지역 변수로 변경된 경우 i

void F()
{
    int i = 1;
    if (i == 2)
        Console.WriteLine("reachable");
}

Console.WriteLine 호출은 실제로 실행되지 않더라도 논리적으로 도달 가능한 것으로 간주됩니다.

끝 메모

함수 멤버 또는 익명 함수의 블록 은 항상 연결할 수 있는 것으로 간주됩니다. 블록에서 각 문의 연결 가능성 규칙을 연속적으로 평가하면 지정된 문의 연결 가능성을 확인할 수 있습니다.

예제: 다음 코드에서

void F(int x)
{
    Console.WriteLine("start");
    if (x < 0)
        Console.WriteLine("negative");
}

두 번째 Console.WriteLine 연결 가능성은 다음과 같이 결정됩니다.

  • 첫 번째 Console.WriteLine 식 문은 메서드 블록 F에 도달할 수 있기 때문에 도달 가능합니다(§13.3).
  • 첫 번째 Console.WriteLine 식 문의 끝점이 도달 가능하기 때문에, 해당 문이 도달 가능하기 때문입니다(§13.7§13.3).
  • 첫 번째 if 식 문의 끝점(Console.WriteLine§13.3)에 도달할 수 있기 때문에 문에도 도달할 수 있습니다.
  • 두 번째 Console.WriteLine 식 문은 if 문에 있는 부울 식에 상수 값 false이 없기 때문에 도달할 수 있습니다.

예제 종료

컴파일 시 문장의 끝점이 도달 가능한 경우 오류가 발생하는 두 가지 상황이 있습니다.

  • switch 문이 스위치 섹션이 다음 스위치 섹션으로 "넘어가는" 것을 허용하지 않기 때문에, 스위치 섹션의 문 목록 끝점이 도달 가능하면 컴파일 시간 오류가 발생합니다. 이 오류가 발생하면 일반적으로 break 문이 누락되었을 가능성을 의미합니다.

  • 값을 계산하는 함수 멤버 블록이나 익명 함수의 끝점이 도달 가능한 경우, 컴파일 시간 오류가 발생합니다. 이 오류가 발생하면 일반적으로 return 명령문이 누락되었음을 나타냅니다 (§13.10.5).

13.3 블록

13.3.1 일반

블록은 단일 문이 허용되는 컨텍스트에서 여러 문을 작성할 수 있도록 허용합니다.

block
    : '{' statement_list? '}'
    ;

블록은 중괄호로 묶인 선택적 statement_list(§13.3.2)로 구성됩니다. 문 목록을 생략하면 블록이 비어 있다고 합니다.

블록에는 선언문(§13.6)이 포함될 수 있습니다. 블록에 선언된 지역 변수 또는 상수의 범위는 블록입니다.

블록은 다음과 같이 실행됩니다.

  • 블록이 비어 있으면 컨트롤이 블록의 끝점으로 전송됩니다.
  • 블록이 비어 있지 않으면 컨트롤이 문 목록으로 전송됩니다. 컨트롤이 문 목록의 끝점에 도달하면 컨트롤이 블록의 끝점으로 전송됩니다.

블록 자체에 도달 가능한 경우 블록의 문장 목록도 도달 가능합니다.

블록이 비어 있거나 문장 목록의 끝점에 도달할 수 있는 경우, 블록의 끝점에 도달할 수 있습니다.

하나 이상의 문을 포함하는 yield(§13.15)을 반복기 블록이라고 합니다. 반복기 블록은 함수 멤버를 반복기로 구현하는 데 사용됩니다(§15.15). 반복기 블록에 몇 가지 추가 제한이 적용됩니다.

  • 반복기 블록에 return 문이 나타나는 경우 컴파일 타임 오류가 발생합니다(하지만 yield return 문은 허용됩니다).
  • 반복기 블록에 안전하지 않은 컨텍스트(§23.2)를 포함하는 것은 컴파일 시간 오류입니다. 반복기 블록은 선언이 안전하지 않은 컨텍스트에 중첩된 경우에도 항상 안전한 컨텍스트를 정의합니다.

13.3.2 문장 목록

문 목록은 순서대로 작성된 하나 이상의 문으로 구성됩니다. 문(statement) 목록은 블록 (§13.3) 및 switch_block (§13.8.3)에 존재합니다.

statement_list
    : statement+
    ;

문 목록은 컨트롤을 첫 번째 문으로 전송하여 실행됩니다. 컨트롤이 문의 끝점에 도달하면 컨트롤이 다음 문으로 전송됩니다. 컨트롤이 마지막 문의 끝점에 도달할 때, 그리고 만약 그럴 경우, 컨트롤은 문 목록의 끝점으로 이동됩니다.

다음 중 하나 이상이 true인 경우 문 목록의 문장이 도달 가능합니다.

  • 문장은 첫 번째 명령문이며, 명령문 목록 자체는 도달 가능합니다.
  • 선행 문장의 끝점에 도달할 수 있습니다.
  • 문은 레이블이 지정된 문이며, 도달 가능한 goto 문에서 그 레이블을 참조합니다.

목록의 마지막 문 끝점에 연결할 수 있는 경우 문 목록의 끝점에 연결할 수 있습니다.

13.4 빈 문장

empty_statement 아무 것도 하지 않습니다.

empty_statement
    : ';'
    ;

문이 필요한 컨텍스트에서 수행할 작업이 없는 경우 빈 문이 사용됩니다.

빈 문을 실행하면 단순히 제어를 문의 끝점으로 전송합니다. 따라서, 빈 문이 도달할 수 있는 경우, 빈 문의 끝점에도 도달할 수 있습니다.

: 본문이 없는 while 문을 작성할 때 빈 문을 사용할 수 있습니다.

bool ProcessMessage() {...}
void ProcessMessages()
{
    while (ProcessMessage())
        ;
}

빈 문을 사용하여 블록의 마지막 "}" 바로 앞에 레이블을 선언할 수도 있습니다.

void F(bool done)
{
    ...
    if (done)
    {
        goto exit;
    }
    ...
  exit:
    ;
}

예제 종료

13.5 레이블이 지정된 문

labeled_statement는 문 앞에 레이블을 붙일 수 있도록 합니다. 레이블이 지정된 문은 블록에서 허용되지만 포함된 문으로 허용되지 않습니다.

labeled_statement
    : identifier ':' statement
    ;

레이블이 지정된 문은 식별자가 지정한 이름으로 레이블을 선언합니다. 레이블의 범위는 중첩된 블록을 포함하여 레이블이 선언되는 전체 블록입니다. 이름이 같은 두 레이블에 겹치는 범위가 있는 경우 컴파일 시간 오류입니다.

레이블 범위 내의 goto의 문장(§13.10.4)에서 레이블을 참조할 수 있습니다.

참고: 이것은 goto 문은 블록 안팎으로 제어를 전달할 수 있지만, 블록으로 전달할 수는 없다는 것을 의미합니다. 끝 메모

레이블에는 자체 선언 공간이 있으며 다른 식별자를 방해하지 않습니다.

예제: 예제

int F(int x)
{
    if (x >= 0)
    {
        goto x;
    }
    x = -x;
  x:
    return x;
}

가 유효하고 이름 x를 매개 변수 및 레이블로 사용합니다.

예제 종료

레이블이 지정된 문의 실행은 레이블 다음에 있는 문의 실행에 정확히 해당합니다.

일반 제어 흐름에서 제공하는 도달 가능성 외에도, 레이블이 도달 가능한 goto 문에 의해 참조되는 경우, 해당 레이블이 지정된 문은 도달 가능성이 있습니다. 다만, 이 경우 goto 문이 도달 불가능한 엔드포인트를 포함하는 try 블록의 catch 블록 또는 finally 블록 내에 있거나, 레이블이 지정된 문이 try_statement 외부에 있지 않을 경우를 제외합니다.

13.6 선언문

13.6.1 일반

declaration_statement 하나 이상의 지역 변수, 하나 이상의 로컬 상수 또는 로컬 함수를 선언합니다. 선언 문은 블록 및 스위치 블록에서는 허용되지만, 다른 문 내에 포함된 문으로는 허용되지 않습니다.

declaration_statement
    : local_variable_declaration ';'
    | local_constant_declaration ';'
    | local_function_declaration
    ;

지역 변수는 local_variable_declaration (§13.6.2)를 사용하여 선언됩니다. 로컬 상수는 local_constant_declaration (§13.6.3)를 사용하여 선언됩니다. 로컬 함수는 local_function_declaration (§13.6.4)를 사용하여 선언됩니다.

선언된 이름은 가장 가까운 바깥쪽 선언 공간(§7.3)에 도입됩니다.

13.6.2 지역 변수 선언

13.6.2.1 일반

local_variable_declaration 하나 이상의 지역 변수를 선언합니다.

local_variable_declaration
    : implicitly_typed_local_variable_declaration
    | explicitly_typed_local_variable_declaration
    | explicitly_typed_ref_local_variable_declaration
    ;

암시적으로 형식화된 선언에는 컨텍스트 키워드(§6.4.4) var 가 포함되어 있으므로 다음과 같이 확인되는 세 가지 범주 간에 구문이 모호합니다.

  • 범위에 명명된 var 형식이 없고 입력이 implicitly_typed_local_variable_declaration 일치하는 경우 선택됩니다.
  • 그렇지 않으면 명명된 var 형식이 범위에 있으면 implicitly_typed_local_variable_declaration 가능한 일치 항목으로 간주되지 않습니다.

local_variable_declaration 내에서 각 변수는 선언자에 의해 도입되며, 이 선언자는 암시적으로 형식화된 경우 implicitly_typed_local_variable_declarator, 명시적으로 형식화된 경우 explicitly_typed_local_variable_declarator, 그리고 ref 지역 변수의 경우 ref_local_variable_declarator 중 하나입니다. 선언자는 도입된 변수의 이름(식별자) 및 초기 값(있는 경우)을 정의합니다.

선언에 여러 선언자가 있는 경우 초기화 식을 포함하여 왼쪽에서 오른쪽으로 순서대로 처리됩니다(§9.4.4.5).

참고: for_initializer(§13.9.4) 또는 resource_acquisition(§13.14)로 발생하지 않는 local_variable_declaration의 경우, 이 왼쪽에서 오른쪽 순서는 각 선언자가 별도의 local_variable_declaration 내에 있는 것과 동일합니다. 다음은 그 예입니다.

void F()
{
    int x = 1, y, z = x * 2;
}

다음과 동일합니다.

void F()
{
    int x = 1;
    int y;
    int z = x * 2;
}

끝 메모

지역 변수의 값은 simple_name 사용하여 식에서 가져옵니다(§12.8.4). 지역 변수는 값을 가져오는 각 위치에 확실히 할당되어야 합니다(§9.4). local_variable_declaration 도입된 각 지역 변수는 처음에 할당되지 않습니다(§9.4.3). 선언자에 초기화 식이 있는 경우 도입된 지역 변수는 선언자 끝에 할당된 것으로 분류됩니다(§9.4.4.5).

local_variable_declaration 도입된 지역 변수의 범위는 다음과 같이 정의됩니다(§7.7).

  • 선언이 for_initializer로 발생하면 범위는 for_initializer, for_condition, for_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?이(가) nullable이 아닌 참조 형식인 경우 T이며, 그렇지 않으면 T입니다. 두 번째 대안은 초기 값을 refvariable_reference로 가진 ref 변수를 선언합니다. ref T?가 nullable이 아닌 참조 형식일 때 그 변수의 형식은 T, 그렇지 않으면 해당 변수의 형식은 ref T입니다. (ref_kind§15.6.1에 설명되어 있습니다.)

예제:

var i = 5;
var s = "Hello";
var d = 1.0;
var numbers = new int[] {1, 2, 3};
var orders = new Dictionary<int,Order>();
ref var j = ref i;
ref readonly var k = ref i;

위의 암시적으로 형식화된 지역 변수 선언은 다음 명시적으로 형식화된 선언과 정확하게 동일합니다.

int i = 5;
string s = "Hello";
double d = 1.0;
int[] numbers = new int[] {1, 2, 3};
Dictionary<int,Order> orders = new Dictionary<int,Order>();
ref int j = ref i;
ref readonly int k = ref i;

다음은 암시적으로 형식화된 잘못된 지역 변수 선언입니다.

var x;                  // Error, no initializer to infer type from
var y = {1, 2, 3};      // Error, array initializer not permitted
var z = null;           // Error, null does not have a type
var u = x => x + 1;     // Error, anonymous functions do not have a type
var v = v++;            // Error, initializer cannot refer to v itself

예제 종료

13.6.2.3 명시적으로 형식화된 지역 변수 선언

explicitly_typed_local_variable_declaration
    : type explicitly_typed_local_variable_declarators
    ;

explicitly_typed_local_variable_declarators
    : explicitly_typed_local_variable_declarator
      (',' explicitly_typed_local_variable_declarator)*
    ;

explicitly_typed_local_variable_declarator
    : identifier ('=' local_variable_initializer)?
    ;

local_variable_initializer
    : expression
    | array_initializer
    ;

explicity_typed_local_variable_declaration 지정된 형식을 가진 하나 이상의 지역 변수를 소개합니다.

local_variable_initializer 있는 경우 해당 형식은 단순 할당(§12.21.2) 또는 배열 초기화(§17.7)의 규칙에 따라 적절해야 하며 해당 값은 변수의 초기 값으로 할당됩니다.

13.6.2.4 명시적으로 형식화된 ref 지역 변수 선언

explicitly_typed_ref_local_variable_declaration
    : ref_kind type ref_local_variable_declarators
    ;

ref_local_variable_declarators
    : ref_local_variable_declarator (',' ref_local_variable_declarator)*
    ;

ref_local_variable_declarator
    : identifier '=' 'ref' variable_reference
    ;

초기화되는 variable_reference타입이어야 하며 ref 할당 (§12.21.3)과 동일한 요구 사항을 충족해야 합니다.

ref_kindref readonly경우 선언되는 식별자는읽기 전용으로 처리되는 변수에 대한 참조입니다. 그렇지 않은 경우 ref_kindref선언되는 식별자는쓰기 가능한 변수에 대한 참조입니다.

method_modifier 선언된 ref struct메서드 내에서 또는 반복기(async) 내에서 ref 지역 변수 또는 형식의 변수를 선언하는 것은 컴파일 시간 오류입니다.

13.6.3 로컬 상수 선언

local_constant_declaration 하나 이상의 로컬 상수가 선언됩니다.

local_constant_declaration
    : 'const' type constant_declarators
    ;

constant_declarators
    : constant_declarator (',' constant_declarator)*
    ;

constant_declarator
    : identifier '=' constant_expression
    ;

local_constant_declaration형식은 선언에 의해 도입된 상수의 형식을 지정합니다. 형식 다음에 constant_declarator 목록이 오며, 각 항목이 새로운 상수를 도입합니다. constant_declarator 상수의 이름을 지정하는 식별자와 "=" 토큰, 상수 값을 제공하는 constant_expression(§12.23)로 구성됩니다.

로컬 상수 선언의 형식constant_expression 상수 멤버 선언(§15.4)과 동일한 규칙을 따라야 합니다.

로컬 상수의 값은 simple_name 사용하여 식에서 가져옵니다(§12.8.4).

로컬 상수의 범위는 선언이 발생하는 블록입니다. constant_declarator 끝 앞에 있는 텍스트 위치에서 로컬 상수의 참조를 참조하는 것은 오류입니다.

여러 상수 선언을 선언하는 로컬 상수 선언은 동일한 형식의 단일 상수의 여러 선언과 동일합니다.

13.6.4 로컬 함수 선언

local_function_declaration 로컬 함수를 선언합니다.

local_function_declaration
    : local_function_modifier* return_type local_function_header
      local_function_body
    | ref_local_function_modifier* ref_kind ref_return_type
      local_function_header ref_local_function_body
    ;

local_function_header
    : identifier '(' parameter_list? ')'
    | identifier type_parameter_list '(' parameter_list? ')'
      type_parameter_constraints_clause*
    ;

local_function_modifier
    : ref_local_function_modifier
    | 'async'
    ;

ref_local_function_modifier
    : 'static'
    | unsafe_modifier   // unsafe code support
    ;

local_function_body
    : block
    | '=>' null_conditional_invocation_expression ';'
    | '=>' expression ';'
    ;

ref_local_function_body
    : block
    | '=>' 'ref' variable_reference ';'
    ;

문법 참고: local_function_body를 인식할 때, null_conditional_invocation_expression 두 가지 대안을 모두 적용할 수 있는 경우 전자를 선택해야 합니다. (§15.6.1)

: 로컬 함수에는 반복기 메서드와 비동기 메서드의 두 가지 일반적인 사용 사례가 있습니다. 반복기 메서드에서는 반환된 시퀀스를 열거하는 코드를 호출할 때만 예외가 관찰됩니다. 비동기 메서드에서는 반환된 작업이 대기 중인 경우에만 예외가 관찰됩니다. 다음 예제에서는 로컬 함수를 사용하여 반복기 구현에서 매개 변수 유효성 검사를 분리하는 방법을 보여 줍니다.

public static IEnumerable<char> AlphabetSubset(char start, char end)
{
    if (start < 'a' || start > 'z')
    {
        throw new ArgumentOutOfRangeException(paramName: nameof(start),
            message: "start must be a letter");
    }
    if (end < 'a' || end > 'z')
    {
        throw new ArgumentOutOfRangeException(paramName: nameof(end),
            message: "end must be a letter");
    }
    if (end <= start)
    {
        throw new ArgumentException(
            $"{nameof(end)} must be greater than {nameof(start)}");
    }
    return AlphabetSubsetImplementation();

    IEnumerable<char> AlphabetSubsetImplementation()
    {
        for (var c = start; c < end; c++)
        {
            yield return c;
        }
    }
}

예제 종료

아래와 달리 지정하지 않는 한 모든 문법 요소의 의미 체계는 메서드 대신 로컬 함수의 컨텍스트에서 읽은 method_declaration (§15.6.1)와 동일합니다.

local_function_declaration식별자는 그 선언된 블록 범위 내에서, 포함하는 지역 변수 선언 공간을 포함하여, 고유해야 합니다. 그 결과 오버로드된 local_function_declaration허용되지 않습니다.

local_function_declaration에서는 하나의 async(§15.14) 한정자와 하나의 unsafe(§23.1) 한정자를 포함할 수 있습니다. 선언에 async 한정자가 포함된 경우, 반환 형식은 void 또는 «TaskType» 형식(§15.14.1)이어야 합니다. 선언에 한정자가 포함된 static 경우 함수는 정적 로컬 함수이고, 그렇지 않으면 비정적 로컬 함수입니다. type_parameter_list 또는 parameter_list속성이 포함되면 컴파일 시간 오류가 발생합니다. 로컬 함수가 안전하지 않은 컨텍스트(§23.2)에서 선언된 경우 로컬 함수 선언에 한정자가 포함되지 않더라도 로컬 함수에 안전하지 않은 코드가 unsafe 포함될 수 있습니다.

로컬 함수는 블록 범위에서 선언됩니다. 비정적 로컬 함수는 바깥쪽 범위에서 변수를 캡처할 수 있지만 정적 로컬 함수는 그렇지 않습니다(따라서 바깥쪽 지역 함수, 매개 변수, 비정적 로컬 함수 또는 this)에 액세스할 수 없습니다. 캡처된 변수가 비정적 로컬 함수의 본문에서 읽지만 함수를 호출하기 전에 확실히 할당되지 않은 경우 컴파일 시간 오류입니다. 컴파일러는 반환 시 확실히 할당되는 변수를 결정해야 합니다(§9.4.4.33).

형식이 this 구조체 형식인 경우, 로컬 함수의 본문에서 this에 접근하려고 하면 컴파일 시 오류가 발생합니다. 이는 액세스가 명시적(this.x)이든 암시적이든(x 구조체의 인스턴스 멤버인 x)이든 마찬가지입니다. 이 규칙은 이러한 액세스만 금지하며 멤버 조회로 인해 구조체의 멤버가 생성되는지 여부에는 영향을 주지 않습니다.

로컬 함수의 본문에 대상이 로컬 함수의 본문 밖에 있는 goto 문, break 문, 또는 continue 문이 포함된다면 그것은 컴파일 시간 오류입니다.

참고: 위의 규칙은 thisgoto의 익명 함수에 대한 규칙을 반영합니다. 끝 메모

로컬 함수는 선언 전에 어휘 지점에서 호출할 수 있습니다. 그러나 로컬 함수(§7.7)에서 사용되는 변수를 선언하기 전에 함수를 어휘적으로 선언하는 것은 컴파일 시간 오류입니다.

로컬 함수가 모든 바깥쪽 지역 변수 선언 공간에 선언된 것과 이름이 같은 매개 변수, 형식 매개 변수 또는 지역 변수를 선언하는 것은 컴파일 시간 오류입니다.

로컬 함수 본문은 항상 접근할 수 있습니다. 로컬 함수 선언의 시작점에 연결할 수 있는 경우 로컬 함수 선언의 엔드포인트에 연결할 수 있습니다.

: 다음 예제에서는 L의 시작점에 도달할 수 없더라도 L의 본문에 도달할 수 있습니다. L의 시작 지점에 접근할 수 없으므로, L의 엔드포인트 이후의 문장에 도달할 수 없습니다.

class C
{
    int M()
    {
        L();
        return 1;

        // Beginning of L is not reachable
        int L()
        {
            // The body of L is reachable
            return 2;
        }
        // Not reachable, because beginning point of L is not reachable
        return 3;
    }
}

즉, 로컬 함수 선언의 위치는 포함하는 함수 내의 문이 접근될 수 있는지 여부에 영향을 미치지 않습니다. 예제 종료

로컬 함수에 대한 인수의 형식이 dynamic인 경우, 호출할 함수는 런타임이 아니라 컴파일 시간에 확인되어야 합니다.

식 트리에서는 로컬 함수를 사용할 수 없습니다.

정적 로컬 함수

  • 포함하는 범위에서 정적 멤버, 형식 매개 변수, 정적 로컬 함수 및 상수 정의를 참조할 수 있습니다.
  • 암시적 this 참조의 인스턴스 멤버나 base 또는 this를 참조하지 못하며, 바깥쪽 범위의 지역 변수, 매개 변수, 또는 비정적 로컬 함수도 참조하지 못합니다. 식에서 nameof() 이러한 모든 것이 허용됩니다.

표현식 문장

expression_statement 지정된 식을 평가합니다. 식에 의해 계산된 값(있는 경우)은 삭제됩니다.

expression_statement
    : statement_expression ';'
    ;

statement_expression
    : null_conditional_invocation_expression
    | invocation_expression
    | object_creation_expression
    | assignment
    | post_increment_expression
    | post_decrement_expression
    | pre_increment_expression
    | pre_decrement_expression
    | await_expression
    ;

모든 표현이 문장으로 허용되는 것은 아닙니다.

참고: 특히 x + yx == 1와 같이 단순히 값을 계산하고(해당 값은 버려집니다) 결과를 문으로 사용하는 식은 허용되지 않습니다. 끝 메모

expression_statement 실행하면 포함된 식이 평가된 다음 expression_statement 끝점으로 제어가 전송됩니다. expression_statement 끝점은 해당 expression_statement에 도달할 수 있는 경우 도달 가능합니다.

13.8 선택문

13.8.1 일반

선택 문은 일부 식의 값에 따라 실행할 수 있는 여러 문 중 하나를 선택합니다.

selection_statement
    : if_statement
    | switch_statement
    ;

13.8.2 조건문

이 문은 if 부울 식의 값에 따라 실행할 문을 선택합니다.

if_statement
    : 'if' '(' boolean_expression ')' embedded_statement
    | 'if' '(' boolean_expression ')' embedded_statement
      'else' embedded_statement
    ;

구문에서 허용되는 어휘적으로 가장 가까운 앞의 else와 연결된 if 부분입니다.

: 따라서 if 형태의 문장입니다.

if (x) if (y) F(); else G();

동일하다

if (x)
{
    if (y)
    {
        F();
    }
    else
    {
        G();
    }
}

예제 종료

if 문은 다음과 같이 실행됩니다.

  • boolean_expression(§12.24)가 평가됩니다.
  • 부울 식이 true이면 제어가 첫 번째 내포된 문장으로 전송됩니다. 제어가 해당 문의 끝점에 도달할 때, 제어가 if 문의 끝점으로 전송됩니다.
  • 부울 표현식이 false를 산출하고 else 부분이 있으면, 제어가 두 번째 포함된 문으로 넘어갑니다. 제어가 해당 문의 끝점에 도달할 때, 제어가 if 문의 끝점으로 전송됩니다.
  • 부울 식이 false를 반환하고 else 부분이 없으면 제어가 if 문의 끝점으로 이동합니다.

if 문의 첫 번째 포함된 문이 도달 가능하기 위해서는, if 문이 도달 가능하고 부울 식이 상수 값을 가지지 않아야 합니다.

if 문장의 두 번째 내포된 문장은, 있는 경우, if 문장이 도달 가능하고 부울 식에 상수 값 true이 없는 경우에 도달 가능합니다.

if의 문이 포함된 문 중 하나 이상의 끝점에 도달할 수 있는 경우 그 문장의 끝점에도 도달할 수 있습니다. 또한 if 문이 연결 가능하고 부울 식이 상수 값 else을 가지지 않는 경우, if 부분이 없는 true 문의 끝점에 도달할 수 있습니다.

13.8.3 switch 문장

이 문은 switch switch 식의 값에 해당하는 연결된 스위치 레이블이 있는 문 목록을 실행하도록 선택합니다.

switch_statement
    : 'switch' '(' expression ')' switch_block
    ;

switch_block
    : '{' switch_section* '}'
    ;

switch_section
    : switch_label+ statement_list
    ;

switch_label
    : 'case' pattern case_guard?  ':'
    | 'default' ':'
    ;

case_guard
    : 'when' expression
    ;

switch_statement 키워드switch와 괄호가 있는 식(switch 식이라고 함) 뒤에 switch_block 구성됩니다. switch_block은 중괄호로 묶인 0개 이상의 switch_section으로 구성됩니다. 각 switch_section 하나 이상의 switch_label뒤에 statement_list (§13.3.2)로 구성됩니다. 각 switch_labelcase와 함께 포함된 스위치 식의 값이 테스트되는 연결된 패턴(§11)을 가집니다. case_guard 있는 경우 해당 식은 형식 bool 으로 암시적으로 변환할 수 있어야 하며 해당 식은 충족된 것으로 간주될 수 있는 추가 조건으로 평가됩니다.

문장의 지배 유형switch은 switch 식에 의해 설정됩니다.

  • switch 식의 형식이 sbyte, ,byte, short,, ushortint, uint, long, ulongcharboolstring또는 enum_type 또는 이러한 형식 중 하나에 해당하는 nullable 값 형식인 경우 해당 명령문의 switch 제어 형식입니다.
  • 그렇지 않은 경우, 스위치 식의 형식에서 다음 가능한 지배 형식 중 하나로 정확히 하나의 사용자 정의 암시적 변환이 있을 경우: sbyte, byte, short, ushort, int, uint, long, ulong, char, string, 또는 해당 형식 중 하나에 해당하는 nullable 값 형식으로 변환된 형식은 switch 문의 지배형입니다.
  • 그렇지 않으면 문의 제어 형식 switch 이 switch 식의 형식입니다. 이러한 형식이 없으면 오류입니다.

default 문장에는 최대 하나의 switch 레이블이 있을 수 있습니다.

입력 식의 형식에 스위치 레이블의 패턴(§11.2.1)을 적용할 수 없는 경우 오류가 발생합니다.

모든 스위치 레이블의 패턴이 case guard가 없거나 case guard가 true 값을 가진 상수 식인 switch 문의 이전 스위치 레이블 패턴 집합(§11.3)에 의해 하위화되면 오류가 발생합니다.

예제:

switch (shape)
{
    case var x:
        break;
    case var _: // error: pattern subsumed, as previous case always matches
        break;
    default:
        break;  // warning: unreachable, all possible values already handled.
}

예제 종료

switch 문은 다음과 같이 실행됩니다.

  • switch 식이 평가되고 관리 형식으로 변환됩니다.
  • 변환된 스위치 식의 값에 따라 컨트롤이 전송됩니다.
    • switch 식의 case 값과 일치하고 가드 식이 없거나 true로 평가되는 동일한 switch 문에 있는 레이블 집합의 어휘 첫 번째 패턴은 일치하는 case 레이블 다음에 컨트롤이 문 목록으로 전송되도록 합니다.
    • 그렇지 않으면 레이블이 default 있으면 컨트롤이 레이블 다음에 default 있는 문 목록으로 전송됩니다.
    • 그렇지 않으면 제어는 switch 문의 끝점으로 전송됩니다.

참고: 런타임 시 패턴이 일치하는 순서는 정의되지 않습니다. 컴파일러는 순서가 맞지 않는 패턴을 일치시키고 이미 일치된 패턴의 결과를 다시 사용하여 다른 패턴의 일치 결과를 계산할 수 있습니다(필수는 아님). 그럼에도 불구하고, 어휘적으로 첫 번째로 식과 일치하며 guard 절이 없거나 true로 평가되는 패턴을 확인하려면 컴파일러가 필요합니다. 끝 메모

스위치 섹션의 문 목록 끝 부분에 도달할 수 있으면 컴파일 시간 오류가 발생합니다. 이를 "탈락 없음" 규칙이라고 합니다.

예제: 예제

switch (i)
{
    case 0:
        CaseZero();
        break;
    case 1:
        CaseOne();
        break;
    default:
        CaseOthers();
        break;
}

는 스위치 섹션에 연결할 수 있는 엔드포인트가 없으므로 유효합니다. C 및 C++와 달리 스위치 섹션의 실행은 다음 스위치 섹션으로 "넘어갈" 수 없습니다.

switch (i)
{
    case 0:
        CaseZero();
    case 1:
        CaseZeroOrOne();
    default:
        CaseAny();
}

컴파일 시간 오류가 발생합니다. 스위치 섹션을 실행한 다음 다른 스위치 섹션을 실행하는 경우 명시적 goto case 또는 goto default 문을 사용해야 합니다.

switch (i)
{
    case 0:
        CaseZero();
        goto case 1;
    case 1:
        CaseZeroOrOne();
        goto default;
    default:
        CaseAny();
        break;
}

예제 종료

switch_section 여러 레이블이 허용됩니다.

예제: 예제

switch (i)
{
    case 0:
        CaseZero();
        break;
    case 1:
        CaseOne();
        break;
    case 2:
    default:
        CaseTwo();
        break;
}

유효합니다. 이 예제는 레이블 case 2:default: 이 동일한 switch_section 일부이므로 "대체 안 함" 규칙을 위반하지 않습니다.

예제 종료

참고: "폴스루 금지" 규칙은 C 및 C++에서 문장이 실수로 생략될 때 일반적으로 발생하는 버그를 방지합니다. 예를 들어 위의 문의 섹션은 switch 문의 동작에 영향을 주지 않고 되돌릴 수 있습니다.

switch (i)
{
    default:
        CaseAny();
        break;
    case 1:
        CaseZeroOrOne();
        goto default;
    case 0:
        CaseZero();
        goto case 1;
}

끝 메모

참고: switch 섹션의 문 목록은 일반적으로 break, goto case 또는 goto default 문으로 끝납니다. 그러나 문 목록의 끝 지점을 도달할 수 없게 만드는 모든 구문이 허용됩니다. 예를 들어, 부울 식 while에 의해 제어되는 true 문은 절대로 끝점에 도달하지 않는 것으로 알려져 있습니다. 마찬가지로, throw 또는 return 문은 항상 컨트롤을 다른 곳으로 전송하고 끝점에 도달하지 않습니다. 따라서 다음 예제는 유효합니다.

switch (i)
{
     case 0:
         while (true)
         {
             F();
         }
     case 1:
         throw new ArgumentException();
     case 2:
         return;
}

끝 메모

: 문을 제어하는 형식 switch은 형식 string일 수 있습니다. 다음은 그 예입니다.

void DoCommand(string command)
{
    switch (command.ToLower())
    {
        case "run":
            DoRun();
            break;
        case "save":
            DoSave();
            break;
        case "quit":
            DoQuit();
            break;
        default:
            InvalidCommand(command);
            break;
    }
}

예제 종료

참고: 문자열 같음 연산자 (§12.12.8)와 마찬가지로, switch 문장은 대/소문자를 구분하며 switch 표현식 문자열이 레이블 상수와 정확히 일치하는 경우에만 해당 switch 섹션을 case 실행합니다. 끝 메모 문의 제어 형식 switchstring 거나 nullable 값 형식인 경우 값 null 은 레이블 상수로 case 허용됩니다.

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

다음 중 하나 이상이 참인 경우 스위치 레이블에 도달할 수 있습니다.

  • switch 식은 상수 값이며 또는
    • 레이블은 case 패턴이 해당 값과 일치하는(§11.2.1) 경우에는 레이블의 가드가 없거나 false 값을 가지는 상수식이 아니라면;
    • 레이블이며 default 스위치 섹션에는 패턴이 해당 값과 일치하고 가드가 없거나 값이 true인 상수 식이 있는 사례 레이블이 포함되어 있지 않습니다.
  • switch 식은 상수 값이 아니며, 또한
    • 레이블은 case 가드가 없거나 값이 상수 false가 아닌 가드가 있는 레이블입니다.
    • 이것은 default 레이블입니다.
      • 스위치 제어 유형에 대해 가드가 없거나 값이 상수 true인 가드가 있는 switch 문의 경우에 나타나는 패턴 집합은 완전하지 않습니다(§11.4) 또는
      • 스위치 관장 형식은 널 허용 형식이며, 가드가 없거나 값이 불리언 true인 가드가 있는 switch 문의 경우에서 나타나는 패턴 집합에는 값 null과 일치하는 패턴이 포함되어 있지 않습니다.
  • 스위치 레이블은 goto case 또는 goto default 문으로 참조됩니다.

지정된 스위치 섹션의 문 목록은 switch 문에 접근할 수 있고, 스위치 섹션에 접근 가능한 스위치 레이블이 포함된 경우 접근할 수 있습니다.

switch 문의 끝점에 도달할 수 있는 경우는 switch 문에 도달할 수 있으며 다음 중 하나 이상이 true일 때입니다.

  • 문에는 switch 문을 종료시키는 도달 가능한 break 문이 포함되어 있습니다.
  • 레이블이 없고, 둘 중 하나가 있습니다.
    • switch 식은 비상수 값이며, 가드가 없거나 가드의 값이 상수 true인 경우를 포함하는 switch 문 케이스의 패턴 집합이 스위치 제어 형식에 대해 포괄적이지 않습니다 (§11.4).
    • switch 식은 nullable 형식의 비상수 값이며, true인 가드가 없는 경우나 그 값과 일치하는 null가드가 없는 switch 문에서는 패턴이 나타나지 않습니다.
    • switch 표현식은 상수 값이며, 가드가 없거나 가드가 true인 어떤 case 레이블도 해당 값과 일치하지 않습니다.

: 다음 코드는 when 절을 간결하게 사용하는 방법을 보여줍니다.

static object CreateShape(string shapeDescription)
{
   switch (shapeDescription)
   {
        case "circle":
            return new Circle(2);
        …
        case var o when string.IsNullOrWhiteSpace(o):
            return null;
        default:
            return "invalid shape description";
    }
}

var 사례는 null, 빈 문자열, 또는 공백만 포함된 문자열과 일치합니다. 예제 종료

13.9 반복 문

13.9.1 일반

반복 문은 포함된 문을 반복적으로 실행합니다.

iteration_statement
    : while_statement
    | do_statement
    | for_statement
    | foreach_statement
    ;

13.9.2 While 문

이 문은 while 조건부로 포함된 문을 0번 이상 실행합니다.

while_statement
    : 'while' '(' boolean_expression ')' embedded_statement
    ;

while 문은 다음과 같이 실행됩니다.

  • boolean_expression(§12.24)가 평가됩니다.
  • 부울 식의 값이 true인 경우, 제어가 내재된 문장으로 전달됩니다. 포함된 문의 끝점에 제어가 도달할 때 및 도달한다면(어쩌면 continue 문의 실행으로 인해), 제어는 while 문의 시작 부분으로 이동합니다.
  • 부울 식이 false로 평가되면, 제어가 while 문의 마지막 단계로 전송됩니다.

while 문에 중첩된 문 내에서 break문(§13.10.2)을 사용하여 제어를 while문 끝점으로 전송할 수 있으며, 이는 중첩된 문 반복을 종료합니다. 또한, continue문(§13.10.3)을 사용하여 중첩된 문 끝점으로 제어를 전송할 수 있어 while문을 다시 반복 수행할 수 있습니다.

while이(가) 연결 가능하고, 부울 식에 상수 값 while이(가) 없는 경우, false 문의 포함된 문에 연결할 수 있습니다.

다음 중 하나 이상이 true인 경우 while 문의 끝점에 도달할 수 있습니다.

  • 문에는 while 문을 종료시키는 도달 가능한 break 문이 포함되어 있습니다.
  • while 문은 도달 가능하고, 부울 식은 상수 값 true이 아닙니다.

13.9.3 do 문

이 문은 do 조건부로 포함된 문을 한 번 이상 실행합니다.

do_statement
    : 'do' embedded_statement 'while' '(' boolean_expression ')' ';'
    ;

do 문은 다음과 같이 실행됩니다.

  • 컨트롤이 포함된 문으로 전송됩니다.
  • 제어가 포함된 문의 끝점에 도달하는 경우(특정 continue 문장의 실행으로 인해) boolean_expression (§12.24)이 평가됩니다. 부울 식이 true를 산출하면, 제어가 do 문의 시작 부분으로 이동합니다. 그렇지 않으면 제어는 do 문의 끝점으로 전송됩니다.

do 문에 중첩된 문 내에서 break문(§13.10.2)을 사용하여 제어를 do문 끝점으로 전송할 수 있으며, 이는 중첩된 문 반복을 종료합니다. 또한, continue문(§13.10.3)을 사용하여 중첩된 문 끝점으로 제어를 전송할 수 있어 do문을 다시 반복 수행할 수 있습니다.

do 문장이 도달할 수 있는 경우, 포함된 문장 do 문장도 도달할 수 있습니다.

다음 중 하나 이상이 true인 경우 do 문의 끝점에 도달할 수 있습니다.

  • 문에는 do 문을 종료시키는 도달 가능한 break 문이 포함되어 있습니다.
  • 포함된 문의 끝점에 연결할 수 있으며 부울 식에 상수 값 true이 없습니다.

13.9.4 For 문

이 문은 for 초기화 식의 시퀀스를 평가한 다음 조건이 true인 동안 포함된 문을 반복적으로 실행하고 반복 식의 시퀀스를 평가합니다.

for_statement
    : 'for' '(' for_initializer? ';' for_condition? ';' for_iterator? ')'
      embedded_statement
    ;

for_initializer
    : local_variable_declaration
    | statement_expression_list
    ;

for_condition
    : boolean_expression
    ;

for_iterator
    : statement_expression_list
    ;

statement_expression_list
    : statement_expression (',' statement_expression)*
    ;

for_initializer 있는 경우 local_variable_declaration(§13.6.2) 또는 쉼표로 구분된 statement_expression목록(§13.7)으로 구성됩니다. for_initializer 선언된 지역 변수의 범위는 for_initializer, for_condition, for_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 문의 끝점으로 전달됩니다.

for 문 내에 포함된 break 문에서 제어를 §13.10.2에 따라 for 문의 끝점으로 전송하여 임베디드 문을 종료할 수 있으며, continue 문에서는 제어를 §13.10.3에 따라 임베디드 문의 끝점으로 전송하여 for_iterator를 실행하고 for에서 시작하여 문의 또 다른 반복을 수행할 수 있습니다.

다음 중 하나가 true인 경우, for 명령문의 포함된 문에 도달할 수 있습니다.

  • for 문에 도달할 수 있으며 for_condition이 존재하지 않습니다.
  • for 문장은 도달 가능하고 for_condition이 있으며 false라는 상수 값을 가지지 않습니다.

다음 중 하나 이상이 true인 경우 for 문의 끝점에 도달할 수 있습니다.

  • 문에는 for 문을 종료시키는 도달 가능한 break 문이 포함되어 있습니다.
  • for 문장은 도달 가능하고 for_condition이 있으며 true라는 상수 값을 가지지 않습니다.

13.9.5 foreach 문

13.9.5.1 일반

이 문은 foreach 컬렉션의 요소를 열거하고 컬렉션의 각 요소에 대해 포함된 문을 실행합니다.

foreach_statement
    : 'await'? 'foreach' '(' ref_kind? local_variable_type identifier
      'in' expression ')' embedded_statement
    ;

foreach 문에서 local_variable_type식별자는 문장의 반복 변수를 선언합니다. var 식별자가 local_variable_type 지정되고 명명된 var 형식이 범위에 없는 경우 반복 변수는 암시적으로 형식화된 반복 변수라고 하며 해당 형식은 아래에 지정된 대로 문의 요소 형식 foreach 으로 간주됩니다.

awaitref_kind가 모두 foreach statement에 있을 경우 컴파일 시간 오류입니다.

foreach_statement 둘 다 포함하거나 포함하지 않는 refreadonly경우 반복 변수는 읽기 전용으로 처리되는 변수를 나타냅니다. 그렇지 않으면, foreach_statementref는 있지만 readonly가 없는 경우, 반복 변수는 쓰기 가능한 변수를 나타냅니다.

반복 변수는 포함된 문을 통해 확장되는 범위가 있는 지역 변수에 해당합니다. 문을 실행하는 foreach 동안 반복 변수는 현재 반복이 수행되고 있는 컬렉션 요소를 나타냅니다. 반복 변수가 읽기 전용 변수를 나타내는 경우 포함된 문이 할당 또는 연산자를 통해 수정하거나 참조 또는 ++-- 출력 매개 변수로 전달하려고 하면 컴파일 시간 오류가 발생합니다.

문장의 컴파일 시간 처리는 먼저 식의 foreach컬렉션 형식, 열거자 형식반복 형식을 선택합니다. 문 처리 foreach§13.9.5.2 에 자세히 설명되어 있으며 해당 프로세스 await foreach§13.9.5.3에 자세히 설명되어 있습니다.

참고: 에 값 null이 있으면 System.NullReferenceException가 런타임에 발생합니다. 끝 메모

구현은 주어진 foreach_statement을 다르게 할 수 있습니다. 예를 들어 성능상의 이유로 상기 확장과 일관된 동작을 유지하는 경우라면 가능합니다.

13.9.5.2 동기 포어치

문장의 컴파일 시간 처리는 먼저 식의 foreach컬렉션 형식, 열거자 형식반복 형식을 선택합니다. 이 결정은 다음과 같이 진행됩니다.

  • 식 형식이 X 배열 형식인 경우 X에서 인터페이스로의 암시적 참조 변환이 있습니다(이 인터페이스를 구현하기 IEnumerable 때문에System.Array). 컬렉션 형식은 인터페이스이고 IEnumerable 열거자 형식은 IEnumerator 인터페이스이고 반복 형식은 배열 형식의 요소 형식 X입니다.
  • X 형식이 인 경우, dynamic에서 IEnumerable 인터페이스(§10.2.10)로 암시적으로 변환됩니다. 컬렉션 형식은 IEnumerable 인터페이스이고 열거자 형식은 인터페이스입니다 IEnumerator . var 식별자가 local_variable_type 지정되면 반복 형식이고dynamic, 그렇지 않으면 다음과 같습니다object.
  • 그렇지 않은 경우 X 형식에 적절한 GetEnumerator 메서드가 있는지 확인합니다.
    • 형식 인수가 없는 형식 X에 대해 식별자 GetEnumerator로 멤버를 조회합니다. 멤버 조회가 일치 항목을 생성하지 않거나 모호성을 생성하거나 메서드 그룹이 아닌 일치 항목을 생성하는 경우 아래 설명된 대로 열거 가능한 인터페이스를 확인합니다. 멤버 조회에서 메서드 그룹 또는 일치 항목이 아닌 다른 결과가 나올 경우 경고를 발행하는 것이 좋습니다.
    • 결과 메서드 그룹 및 빈 인수 목록을 사용하여 오버로드 확인을 수행합니다. 오버로드 확인으로 인해 적용 가능한 메서드가 없거나 모호성이 발생하거나 단일 최상의 메서드가 생성되지만 해당 메서드가 정적이거나 공용이 아닌 경우 아래 설명된 대로 열거 가능한 인터페이스를 확인합니다. 오버로드 확인에서 명확한 퍼블릭 인스턴스 메서드를 제외한 모든 항목이 생성되거나 적용 가능한 메서드가 없는 경우 경고를 발생시키는 것이 좋습니다.
    • E 메서드의 반환 형식 GetEnumerator 클래스, 구조체 또는 인터페이스 형식이 아닌 경우 오류가 생성되고 추가 단계가 수행되지 않습니다.
    • 멤버 조회는 형식 인수가 없이 E에서 식별자 Current로 수행됩니다. 멤버 조회에서 일치하는 항목이 생성되지 않거나, 결과가 오류이거나, 읽기를 허용하는 공용 인스턴스 속성을 제외한 모든 항목이 결과인 경우 오류가 생성되고 추가 단계가 수행되지 않습니다.
    • 멤버 조회는 형식 인수가 없이 E에서 식별자 MoveNext로 수행됩니다. 멤버 조회에서 일치하는 항목이 생성되지 않거나, 결과가 오류이거나, 결과가 메서드 그룹을 제외한 모든 항목인 경우 오류가 생성되고 추가 단계가 수행되지 않습니다.
    • 오버로드 확인은 빈 인수 목록을 사용하여 메서드 그룹에서 수행됩니다. 오버로드 확인으로 인해 적용 가능한 메서드가 없거나 모호성이 발생하거나 단일 최상의 메서드가 발생하지만 해당 메서드가 정적이거나 public이 아니거나 반환 형식이 아닌 경우 오류가 생성되고 추가 단계가 수행되지 않습니다 bool.
    • 컬렉션 형식이 X, 열거자 형식이 E, 반복 형식은 Current 속성의 형식입니다. 속성에는 Current 한정자가 포함될 ref 수 있습니다. 이 경우 반환되는 식은 선택적으로 읽기 전용인 variable_reference (§9.5)입니다.
  • 그렇지 않으면 열거 가능한 인터페이스를 확인합니다.
    • Tᵢ에서 X로의 암시적 변환이 가능한 모든 형식 IEnumerable<Tᵢ> 중에서, TT가 아니며 다른 dynamic에 대해서도 Tᵢ에서 IEnumerable<T>로의 암시적 변환이 존재하는 유일한 형식 IEnumerable<Tᵢ>가 있다면, 컬렉션 형식은 인터페이스 IEnumerable<T>이고, 열거자 형식은 인터페이스 IEnumerator<T>이며, 반복 형식은 T입니다.
    • 그렇지 않으면 이러한 형식이 T두 개 이상 있는 경우 오류가 생성되고 추가 단계가 수행되지 않습니다.
    • 그렇지 않으면 인터페이스로 XSystem.Collections.IEnumerable 의 암시적 변환이 있는 경우 컬렉션 형식은 이 인터페이스이고 열거자 형식은 인터페이스 System.Collections.IEnumerator이고 반복 형식은 다음과 같습니다 object.
    • 그렇지 않으면 오류가 생성되고 추가 단계가 수행되지 않습니다.

위의 단계가 성공적이면 컬렉션 형식 C, 열거자 형식 E, 그리고 반복 형식 T, ref T, 또는 ref readonly T을 명확하게 생성합니다. foreach 형식의 문장입니다.

foreach (V v in x) «embedded_statement»

는 다음과 같습니다.

{
    E e = ((C)(x)).GetEnumerator();
    try
    {
        while (e.MoveNext())
        {
            V v = (V)(T)e.Current;
            «embedded_statement»
        }
    }
    finally
    {
        ... // Dispose e
    }
}

변수 e 는 식 x 이나 포함된 문 또는 프로그램의 다른 소스 코드에 표시되거나 액세스할 수 없습니다. 변수 v 는 포함된 문에서 읽기 전용입니다. 명시적 변환(§10.3)이 T (반복 형식)에서 V ( 문에서의 foreach)으로 없으면 오류가 발생하고 추가 단계는 수행되지 않습니다.

반복 변수가 참조 변수(§9.7)인 경우, foreach 형태의 문입니다.

foreach (ref V v in x) «embedded_statement»

는 다음과 같습니다.

{
    E e = ((C)(x)).GetEnumerator();
    try
    {
        while (e.MoveNext())
        {
            ref V v = ref e.Current;
            «embedded_statement»
        }
    }
    finally
    {
        ... // Dispose e
    }
}

변수 e 는 식 x 이나 포함된 문 또는 프로그램의 다른 소스 코드에서 볼 수 없거나 액세스할 수 없습니다. 참조 변수 v 는 포함된 문에서 읽기/쓰기가 가능하지만 v ref-reassigned(§12.21.3)해서는 안 됩니다. 정체성 변환(§10.2.2)이 T (반복 유형)에서 V (문의 foreach로 변환되지 않으면 오류가 발생하고 추가 단계가 수행되지 않습니다.

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

v 루프 내부에 while를 배치하는 것은 embedded_statement에서 발생하는 익명 함수에 의해 캡처되는 방법(§12.19.6.2)에 중요합니다.

예제:

int[] values = { 7, 9, 13 };
Action f = null;
foreach (var value in values)
{
    if (f == null)
    {
        f = () => Console.WriteLine("First value: " + value);
    }
}
f();

확장된 형태에서 vwhile 루프 외부에서 선언되면, 이는 모든 반복에서 공유되며, for 루프가 끝난 후의 값은 최종 값13이 됩니다. 이 값은 f의 호출 시 인쇄되는 값입니다. 대신 각 반복에 고유한 변수 v가 있으므로 첫 번째 반복에서 캡처한 f 변수는 계속 값을 저장합니다. 이 값 7은 인쇄됩니다. 이전 버전의 C#에서는 vwhile 루프 외부에서 선언되었습니다.

예제 종료

finally 블록의 본문은 다음 단계에 따라 생성됩니다.

  • E의 암시적 변환이 System.IDisposable 인터페이스로 되는 경우

    • E가 비nullable 값 형식이면 finally 절은 다음과 같은 의미로 확장됩니다.

      finally
      {
          ((System.IDisposable)e).Dispose();
      }
      
    • 그렇지 않으면 finally 절이 다음과 같은 의미 체계로 확장됩니다.

      finally
      {
          System.IDisposable d = e as System.IDisposable;
          if (d != null)
          {
              d.Dispose();
          }
      }
      

      단, E가 값 형식이거나 값 형식으로 인스턴스화된 형식 매개 변수인 경우 eSystem.IDisposable로 변환해도 박싱이 발생하지 않습니다.

  • 그렇지 않으면 E이(가) 봉인된 형식인 경우에 finally 절이 빈 블록으로 확장됩니다.

    finally {}
    
  • 그렇지 않으면 절이 finally 다음으로 확장됩니다.

    finally
    {
        System.IDisposable d = e as System.IDisposable;
        if (d != null)
        {
            d.Dispose();
        }
    }
    

지역 변수 d 는 사용자 코드에 표시되거나 액세스할 수 없습니다. 특히 범위가 블록을 포함하는 finally 다른 변수와 충돌하지 않습니다.

배열의 요소를 트래버스하는 foreach 순서는 다음과 같습니다. 1차원 배열의 경우 인덱스 0부터 시작하여 인덱스로 Length – 1끝나는 인덱스 순서로 요소가 트래버스됩니다. 다차원 배열의 경우, 요소들은 먼저 가장 오른쪽 차원의 인덱스가 증가하고, 그 다음으로 왼쪽에 위치한 차원의 인덱스가 증가하는 방식으로 순회됩니다.

예제: 다음 예제에서는 각 값을 요소 순서대로 2차원 배열로 출력합니다.

class Test
{
    static void Main()
    {
        double[,] values =
        {
            {1.2, 2.3, 3.4, 4.5},
            {5.6, 6.7, 7.8, 8.9}
        };
        foreach (double elementValue in values)
        {
            Console.Write($"{elementValue} ");
        }
        Console.WriteLine();
    }
}

생성된 출력은 다음과 같습니다.

1.2 2.3 3.4 4.5 5.6 6.7 7.8 8.9

예제 종료

: 다음의 예제에서

int[] numbers = { 1, 3, 5, 7, 9 };
foreach (var n in numbers)
{
    Console.WriteLine(n);
}

유추된 n의 형식은 int의 반복 형식인 numbers으로 간주됩니다.

예제 종료

13.9.5.3 foreach 대기

문장의 컴파일 시간 처리는 먼저 식의 foreach컬렉션 형식, 열거자 형식반복 형식을 선택합니다. 문 처리 foreach§13.9.5.2 에 자세히 설명되어 있으며 해당 프로세스 await foreach§13.9.5.3에 자세히 설명되어 있습니다.

이 결정은 다음과 같이 진행됩니다.

  • 형식 X 에 적절한 GetAsyncEnumerator 메서드가 있는지 확인합니다.
    • 형식 인수가 없는 형식 X에 대해 식별자 GetAsyncEnumerator로 멤버를 조회합니다. 멤버 조회가 일치 항목을 생성하지 않거나 모호성을 생성하거나 메서드 그룹이 아닌 일치 항목을 생성하는 경우 아래 설명된 대로 열거 가능한 인터페이스를 확인합니다. 멤버 조회에서 메서드 그룹 또는 일치 항목이 아닌 다른 결과가 나올 경우 경고를 발행하는 것이 좋습니다.
    • 결과 메서드 그룹 및 빈 인수 목록을 사용하여 오버로드 확인을 수행합니다. 오버로드 확인으로 인해 적용 가능한 메서드가 없거나 모호성이 발생하거나 단일 최상의 메서드가 생성되지만 해당 메서드가 정적이거나 공용이 아닌 경우 아래 설명된 대로 열거 가능한 인터페이스를 확인합니다. 오버로드 확인에서 명확한 퍼블릭 인스턴스 메서드를 제외한 모든 항목이 생성되거나 적용 가능한 메서드가 없는 경우 경고를 발생시키는 것이 좋습니다.
    • E 메서드의 반환 형식 GetAsyncEnumerator 클래스, 구조체 또는 인터페이스 형식이 아닌 경우 오류가 생성되고 추가 단계가 수행되지 않습니다.
    • 멤버 조회는 형식 인수가 없이 E에서 식별자 Current로 수행됩니다. 멤버 조회에서 일치하는 항목이 생성되지 않거나, 결과가 오류이거나, 읽기를 허용하는 공용 인스턴스 속성을 제외한 모든 항목이 결과인 경우 오류가 생성되고 추가 단계가 수행되지 않습니다.
    • 멤버 조회는 형식 인수가 없이 E에서 식별자 MoveNextAsync로 수행됩니다. 멤버 조회에서 일치하는 항목이 생성되지 않거나, 결과가 오류이거나, 결과가 메서드 그룹을 제외한 모든 항목인 경우 오류가 생성되고 추가 단계가 수행되지 않습니다.
    • 오버로드 확인은 빈 인수 목록을 사용하여 메서드 그룹에서 수행됩니다. 오버로드 확인으로 인해 적용 가능한 메서드가 없거나 모호성이 발생하거나 단일 최상의 메서드가 발생하지만 해당 메서드가 정적이거나 공용이 아니거나 반환 형식이 대기할 수 없거나(§12.9.8.2) await_expression (bool)로 분류되는 경우 오류가 생성되고 추가 단계가 수행되지 않습니다.
    • 컬렉션 형식이 X, 열거자 형식이 E, 반복 형식은 Current 속성의 형식입니다.
  • 그렇지 않으면 비동기 열거 가능한 인터페이스를 확인합니다.
    • Tᵢ에서 X로의 암시적 변환이 가능한 모든 형식 IAsyncEnumerable<Tᵢ> 중에서, TT가 아니며 다른 dynamic에 대해서도 Tᵢ에서 IAsyncEnumerable<T>로의 암시적 변환이 존재하는 유일한 형식 IAsyncEnumerable<Tᵢ>가 있다면, 컬렉션 형식은 인터페이스 IAsyncEnumerable<T>이고, 열거자 형식은 인터페이스 IAsyncEnumerator<T>이며, 반복 형식은 T입니다.
    • 그렇지 않으면 이러한 형식이 T두 개 이상 있는 경우 오류가 생성되고 추가 단계가 수행되지 않습니다.
    • 그렇지 않으면 오류가 생성되고 추가 단계가 수행되지 않습니다.

위의 단계는 성공적이면 컬렉션 형식 C, 열거자 형식 E 및 반복 형식 T명확하게 생성합니다. await foreach 형식으로 된 문장

await foreach (V v in x) «embedded_statement»

는 다음과 같습니다.

{
    E e = ((C)(x)).GetAsyncEnumerator();
    try
    {
        while (await e.MoveNextAsync())
        {
            V v = (V)(T)e.Current;
            «embedded_statement»
        }
    }
    finally
    {
        ... // Dispose e
    }
}

변수 e 는 식 x 이나 포함된 문 또는 프로그램의 다른 소스 코드에 표시되거나 액세스할 수 없습니다. 변수 v 는 포함된 문에서 읽기 전용입니다. 명시적 변환(§10.3)이 T (반복 형식)에서 V ( 문에서의 await foreach)으로 없으면 오류가 발생하고 추가 단계는 수행되지 않습니다.

비동기 열거자는 선택적으로 인수 없이 호출할 수 있고 DisposeAsync될 수 있는 메서드를 노출시키며, 이 메서드의 awaitGetResult()를 반환할 수 있습니다.

foreach 형식의 문장입니다.

await foreach (T item in enumerable) «embedded_statement»

는 다음으로 확장됩니다.

var enumerator = enumerable.GetAsyncEnumerator();
try
{
    while (await enumerator.MoveNextAsync())
    {
       T item = enumerator.Current;
       «embedded_statement»
    }
}
finally
{
    await enumerator.DisposeAsync(); // omitted, along with the try/finally,
                            // if the enumerator doesn't expose DisposeAsync
}

13.10 점프 문장

13.10.1 일반

점프 문은 무조건 제어를 전송합니다.

jump_statement
    : break_statement
    | continue_statement
    | goto_statement
    | return_statement
    | throw_statement
    ;

jump 문이 컨트롤을 전송하는 위치를 jump 문의 대상 이라고 합니다.

점프 문이 블록 내에서 발생하고 해당 점프 문의 대상이 해당 블록 외부에 있는 경우, 점프 문은 블록을 종료 한다고 합니다. jump 문은 블록 외부로 제어를 전송할 수 있지만 제어를 블록으로 전송할 수 없습니다.

jump 문의 실행은 중간에 끼어드는 try 문 때문에 복잡해집니다. 이러한 try 명령문이 없는 경우 jump 문은 점프 문에서 해당 대상으로 제어를 무조건 전송합니다. 이러한 중간 try 문이 있는 경우 실행이 더 복잡합니다. jump 문이 연결된 try 블록과 함께 하나 이상의 finally 블록을 종료하는 경우, 컨트롤은 처음에 가장 안쪽 finally 문의 try 블록으로 전송됩니다. 컨트롤이 finally 블록의 끝점에 도달할 때, 컨트롤은 다음 바깥의 finally 문장의 try 블록으로 전송됩니다. 이 프로세스는 모든 중간 finally 문의 블록이 실행될 때까지 try 반복됩니다.

예제: 다음 코드에서

class Test
{
    static void Main()
    {
        while (true)
        {
            try
            {
                try
                {
                    Console.WriteLine("Before break");
                    break;
                }
                finally
                {
                    Console.WriteLine("Innermost finally block");
                }
            }
            finally
            {
                Console.WriteLine("Outermost finally block");
            }
        }
        Console.WriteLine("After break");
    }
}

제어 흐름이 finally 점프 문의 대상으로 전송되기 전에 두 try 문과 연결된 블록이 실행됩니다. 생성된 출력은 다음과 같습니다.

Before break
Innermost finally block
Outermost finally block
After break

예제 종료

13.10.2 break 문

break 문은 가장 가까운 switch, while, do, for, 또는 foreach 문에서 빠져나옵니다.

break_statement
    : 'break' ';'
    ;

가장 가까운 인접한 break, switch, while, do, 또는 for 문장의 끝점이 foreach 문장의 대상입니다. break 문이 switch, while, do, for 또는 foreach 문으로 묶이지 않으면 컴파일 시간 오류가 발생합니다.

여러 switch, while, do, for, 또는 foreach 문이 서로 중첩되는 경우, break 문은 가장 안쪽 문장에만 적용됩니다. 여러 중첩 수준에서 goto 제어를 전송하기 위해 문(§13.10.4)을 사용해야 합니다.

break 구문은 finally 블록을 종료할 수 없습니다(§13.11). break 문이 finally 블록 내에서 발생하면, break 문의 대상은 반드시 동일한 finally 블록 내에 있어야 합니다. 그렇지 않으면 컴파일 시 오류가 발생합니다.

break 문은 다음과 같이 실행됩니다.

  • break 문이 하나 이상의 try 블록과 이에 연결된 finally 블록을 종료하는 경우, 컨트롤은 처음에 가장 안쪽 finally 문의 try 블록으로 전송됩니다. 컨트롤이 finally 블록의 끝점에 도달할 때, 컨트롤은 다음 바깥의 finally 문장의 try 블록으로 전송됩니다. 이 프로세스는 모든 중간 finally 문의 블록이 실행될 때까지 try 반복됩니다.
  • 제어는 break 문장의 대상으로 이동됩니다.

break 문이 무조건 제어를 다른 곳으로 전송하기 때문에 break 문 끝점이 결코 도달하지 않습니다.

13.10.3 continue 문

continue 문은 가장 가까운 바깥쪽 while, do, for, 또는 foreach 문의 새 반복을 시작합니다.

continue_statement
    : 'continue' ';'
    ;

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

여러 while, do, for, 또는 foreach 문이 서로 중첩된 경우, continue 문은 가장 안쪽 문에만 적용됩니다. 여러 중첩 수준에서 goto 제어를 전송하기 위해 문(§13.10.4)을 사용해야 합니다.

continue 구문은 finally 블록을 종료할 수 없습니다(§13.11). continue 문이 finally 블록 내에서 발생하면, continue 문의 대상은 반드시 동일한 finally 블록 내에 있어야 합니다. 그렇지 않으면 컴파일 시 오류가 발생합니다.

continue 문은 다음과 같이 실행됩니다.

  • continue 문이 하나 이상의 try 블록과 이에 연결된 finally 블록을 종료하는 경우, 컨트롤은 처음에 가장 안쪽 finally 문의 try 블록으로 전송됩니다. 컨트롤이 finally 블록의 끝점에 도달할 때, 컨트롤은 다음 바깥의 finally 문장의 try 블록으로 전송됩니다. 이 프로세스는 모든 중간 finally 문의 블록이 실행될 때까지 try 반복됩니다.
  • 제어는 continue 문장의 대상으로 이동됩니다.

continue 문이 무조건 제어를 다른 곳으로 전송하기 때문에 continue 문 끝점이 결코 도달하지 않습니다.

13.10.4 goto 문

이 문은 goto 레이블로 표시된 문으로 컨트롤을 전송합니다.

goto_statement
    : 'goto' identifier ';'
    | 'goto' 'case' constant_expression ';'
    | 'goto' 'default' ';'
    ;

goto 식별자 문의 대상은 지정된 레이블을 가진 레이블 문입니다. 지정된 이름의 레이블이 현재 함수 멤버에 없거나 문이 레이블 범위 내에 없는 경우 goto 컴파일 시간 오류가 발생합니다.

참고: 이 규칙은 문을 사용하여 goto 중첩된 범위 에서 제어를 전송할 수 있지만 중첩된 범위 로는 전송할 수 없습니다. 예제에서

class Test
{
    static void Main(string[] args)
    {
        string[,] table =
        {
            {"Red", "Blue", "Green"},
            {"Monday", "Wednesday", "Friday"}
        };
        foreach (string str in args)
        {
            int row, colm;
            for (row = 0; row <= 1; ++row)
            {
                for (colm = 0; colm <= 2; ++colm)
                {
                    if (str == table[row,colm])
                    {
                        goto done;
                    }
                }
            }
            Console.WriteLine($"{str} not found");
            continue;
          done:
            Console.WriteLine($"Found {str} at [{row}][{colm}]");
        }
    }
}

goto 은 중첩된 범위에서 벗어나 제어를 전환하는 데 사용됩니다.

끝 메모

문의 대상은 지정된 상수 값의 goto case 상수 패턴과 가드가 없는 레이블을 포함하는 switch 즉시 바깥쪽 문(case)의 문 목록입니다. goto case 문이 switch 문으로 묶여 있지 않거나, 가장 가까운 바깥쪽 switch 문에 그러한 case가 포함되어 있지 않거나, 또는 constant_expression이 가장 가까운 바깥쪽 문의 제어 형식으로 암시적으로 변환할 수 없는 경우(switch), 컴파일 시간 오류가 발생합니다.

즉시 바깥쪽 goto default 문(switch) 내의 문 목록을 대상으로 하는 문의 대상은 default 레이블을 포함한 것입니다. goto default 문이 switch 문에 의해 둘러싸여 있지 않거나, 가장 가까운 둘러싸고 있는 switch 문에 default 레이블이 포함되어 있지 않으면 컴파일 오류가 발생합니다.

goto 구문은 finally 블록을 종료할 수 없습니다(§13.11). goto 문이 블록 finally 내에서 발생하면, goto 문의 대상은 동일한 finally 블록 내에 있어야 하며, 그렇지 않을 경우 컴파일 시간 오류가 발생합니다.

goto 문은 다음과 같이 실행됩니다.

  • goto 문이 하나 이상의 try 블록과 이에 연결된 finally 블록을 종료하는 경우, 컨트롤은 처음에 가장 안쪽 finally 문의 try 블록으로 전송됩니다. 컨트롤이 finally 블록의 끝점에 도달할 때, 컨트롤은 다음 바깥의 finally 문장의 try 블록으로 전송됩니다. 이 프로세스는 모든 중간 finally 문의 블록이 실행될 때까지 try 반복됩니다.
  • 제어는 goto 문장의 대상으로 이동됩니다.

goto 문이 무조건 제어를 다른 곳으로 전송하기 때문에 goto 문 끝점이 결코 도달하지 않습니다.

13.10.5 return 문

이 문은 return 반환 문이 나타나는 함수 멤버의 현재 호출자에게 컨트롤을 반환하며, 필요에 따라 값 또는 variable_reference (§9.5)를 반환합니다.

return_statement
    : 'return' ';'
    | 'return' expression ';'
    | 'return' 'ref' variable_reference ';'
    ;

이 없는 return_statement반환값 없음(return-no-value)이라고 하며, ref을 포함하는 경우는 참조로 반환(return-by-ref)이라 하고, 식만 포함하는 경우는 값으로 반환(return-by-value)한다고 합니다.

반환 값을 제공하는 것으로 선언된 메서드나 반환을 참조하는 메서드(§15.6.1)에서 return-no-value를 사용하는 것은 컴파일 시간 오류입니다.

값을 반환하지 않거나 값을 반환하는 것으로 선언된 메서드에서 return-by-ref를 사용하는 것은 컴파일 시간 오류입니다.

return-no-value 또는 return-by-ref로 선언된 메서드의 반환 값을 사용하는 것은 컴파일 시간 오류입니다.

variable_reference 아니거나 ref-safe-context가 호출자 컨텍스트(§9.7.2)가 아닌 변수에 대한 참조인 경우 return-by-ref를 사용하는 것은 컴파일 시간 오류입니다.

method_modifierasync 선언된 메서드의 return-by-ref를 사용하는 것은 컴파일 시간 오류입니다.

함수 멤버는 값별 반환 메서드(§15.6.11), 속성 또는 인덱서의 값별 반환 가져오기 접근자 또는 사용자 정의 연산자가 있는 메서드인 경우 값을 계산하는 것으로 합니다. 값을 반환하지 않는 함수 멤버는 값을 계산하지 않으며 유효 반환 형식을 가진 메서드, 속성 및 인덱서의 접근자 설정, 이벤트의 접근자 추가 및 제거, 인스턴스 생성자, 정적 생성자 및 종료자로 구성됩니다. return-by-ref인 함수 멤버는 값을 계산하지 않습니다.

값 반환의 경우, 의 형식에서 포함된 함수 멤버의 유효 반환 형식(§15.6.11)으로 암시적 변환(§10.2)이 존재해야 합니다. return-by-ref의 경우 식 형식과 포함하는 함수 멤버의 유효 반환 형식 사이에 ID 변환(§10.2.2)이 있어야 합니다.

return 무명 함수 식(§12.19)의 본문에도 문을 사용하고 해당 함수에 대해 존재하는 변환을 결정하는 데 참여할 수 있습니다(§10.7.1).

return 문이 finally 블록에 나타나면 컴파일 시간 오류가 발생합니다(§13.11).

return 문은 다음과 같이 실행됩니다.

  • 반환 값의 경우 이 계산되고 해당 값이 암시적 변환을 통해 포함된 함수의 실제 반환 유형으로 변환됩니다. 변환 결과는 함수에 의해 생성된 결과 값이 됩니다. return-by-ref의 경우 이 평가되고 결과는 변수로 분류되어야 합니다. 바깥쪽 메서드의 return-by-ref에 readonly가 포함된 경우에 결과 변수는 읽기 전용입니다.
  • return 문이 하나 이상의 try 또는 catch 블록에 finally 블록이 연결된 형태로 포함되어 있는 경우, 컨트롤은 처음에 가장 안쪽 finally 문의 try 블록으로 전송됩니다. 컨트롤이 finally 블록의 끝점에 도달할 때, 컨트롤은 다음 바깥의 finally 문장의 try 블록으로 전송됩니다. 이 프로세스는 모든 바깥쪽 finally 문의 블록이 실행될 때까지 try 반복됩니다.
  • 포함하는 함수가 비동기 함수가 아닌 경우 컨트롤은 결과 값과 함께 포함하는 함수의 호출자에게 반환됩니다(있는 경우).
  • 포함하는 함수가 비동기 함수인 경우 컨트롤은 현재 호출자에게 반환되고 결과 값(있는 경우)은 (§15.14.3)에 설명된 대로 반환 작업에 기록됩니다.

return 문이 무조건 제어를 다른 곳으로 전송하기 때문에 return 문 끝점이 결코 도달하지 않습니다.

13.10.6 throw 문

문장은 throw 예외를 발생시킵니다.

throw_statement
    : 'throw' expression? ';'
    ;

throw 식이 있는 문은 식을 계산하여 생성된 예외를 throw합니다. 식은 암시적으로 System.Exception으로 변환될 수 있어야 하며, 식을 계산한 결과는 throw하기 전에 System.Exception으로 변환됩니다. 변환의 결과가 null이면, 대신 System.NullReferenceException가 발생합니다.

표현식이 없는 구문은 throw 블록 안에서만 사용할 수 있습니다. 이 경우, 해당 구문은 그 블록에서 현재 처리 중인 예외를 다시 던집니다.

throw 문이 무조건 제어를 다른 곳으로 전송하기 때문에 throw 문 끝점이 결코 도달하지 않습니다.

예외가 throw되면 제어는 예외를 처리할 수 있는 감싸고 있는 catch 문장의 첫 번째 try 절로 전송됩니다. throw되는 예외 지점에서 적절한 예외 처리기로 제어를 전송하는 지점까지 발생하는 프로세스를 예외 전파라고 합니다. 예외 전파는 예외와 일치하는 절이 발견될 때까지 catch 다음 단계를 반복적으로 평가하는 것으로 구성됩니다. 이 설명에서 throw 지점은 예외가 발생하는 초기 위치를 나타냅니다. 이 동작은 (§21.4)에 지정됩니다.

  • 현재 함수 멤버에서는 throw 지점을 포함하는 각 try 문이 검사됩니다. 각 문 S에 대해 가장 try 안쪽 문으로 시작하고 가장 try 바깥쪽 문으로 끝나는 다음 단계가 평가됩니다.

    • try 블록이 S throw 지점을 감싸고, S에 하나 이상의 catch 절이 있는 경우, catch 절은 예외에 적합한 처리기를 찾기 위해 나타나는 순서대로 검사됩니다. 런타임 시 예외 형식 catch을 나타내는 형식 매개 변수 또는 예외 형식 T을 지정하는 첫 번째 T 절에서 E의 런타임 형식이 T으로부터 파생된 것으로 간주될 때 일치한다고 봅니다. 절에 예외 필터가 포함된 경우 예외 개체가 예외 변수에 할당되고 예외 필터가 평가됩니다. catch 절에 예외 필터가 포함되어 있을 때, 예외 필터가 catch로 평가되면 해당 true 절은 일치 항목으로 간주됩니다. 일반 catch (§13.11) 절은 모든 예외 형식에 대한 일치 항목으로 간주됩니다. 일치하는 catch 절이 발견되면, 예외 전파는 해당 절의 블록으로 제어를 넘기는 것으로 catch 완료됩니다.
    • 그렇지 않으면, try 블록이나 catch 블록이 S throw 지점을 포함하고, 또한 Sfinally 블록이 있다면, 제어는 finally 블록으로 전송됩니다. finally 블록이 다른 예외를 throw하면 현재 예외 처리가 종료됩니다. 그렇지 않으면 컨트롤이 블록의 finally 끝점에 도달하면 현재 예외 처리가 계속됩니다.
  • 예외 처리기가 현재 함수 호출에 없는 경우 함수 호출이 종료되고 다음 중 하나가 발생합니다.

    • 현재 함수가 비동기인 경우 함수 멤버가 호출된 문에 해당하는 throw 지점이 있는 함수 호출자에 대해 위의 단계가 반복됩니다.

    • 현재 함수가 비동기 및 작업 반환인 경우 예외는 반환 태스크에 기록됩니다. 이 작업은 §15.14.3에 설명된 대로 오류 또는 취소된 상태로 전환됩니다.

    • 현재 함수가 비동기 및 void-returning이면 §15.14.4에 설명된 대로 현재 스레드의 동기화 컨텍스트에 알림이 표시됩니다.

  • 예외 처리가 현재 스레드의 모든 함수 멤버 호출을 종료하여 스레드에 예외 처리기가 없음을 나타내는 경우 스레드 자체는 종료됩니다. 이러한 종료의 영향은 구현에서 정의됩니다.

13.11 try 문

이 문은 try 블록을 실행하는 동안 발생하는 예외를 catch하기 위한 메커니즘을 제공합니다. 또한 문장 try은 제어가 문장 try을 떠날 때 항상 실행되는 코드 블록을 지정할 수 있는 기능을 제공합니다.

try_statement
    : 'try' block catch_clauses
    | 'try' block catch_clauses? finally_clause
    ;

catch_clauses
    : specific_catch_clause+
    | specific_catch_clause* general_catch_clause
    ;

specific_catch_clause
    : 'catch' exception_specifier exception_filter? block
    | 'catch' exception_filter block
    ;

exception_specifier
    : '(' type identifier? ')'
    ;

exception_filter
    : 'when' '(' boolean_expression ')'
    ;

general_catch_clause
    : 'catch' block
    ;

finally_clause
    : 'finally' block
    ;

try_statementtry 키워드와 블록, 0개 이상의 catch_clauses, 그리고 선택적인 finally_clause로 구성됩니다. 하나 이상의 catch_clause 또는 finally_clause 있어야 합니다.

exception_specifier형식 또는 유효 기본 클래스(type_parameter 경우)이거나 해당 형식에서 파생되는 형식이어야 System.Exception 합니다.

절이 catchclass_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 블록 내에서는 식이 없는 throw 문(§13.10.6)을 사용하여 catch 블록에서 catch한 예외를 다시 throw할 수 있습니다. 예외 변수에 대한 할당은 다시 throw되는 예외를 변경하지 않습니다.

예제: 다음 코드에서

class Test
{
    static void F()
    {
        try
        {
            G();
        }
        catch (Exception e)
        {
            Console.WriteLine("Exception in F: " + e.Message);
            e = new Exception("F");
            throw; // re-throw
        }
    }

    static void G() => throw new Exception("G");

    static void Main()
    {
        try
        {
            F();
        }
        catch (Exception e)
        {
            Console.WriteLine("Exception in Main: " + e.Message);
        }
    }
}

메서드 F는 예외를 포착하고, 일부 진단 정보를 콘솔에 기록하며, 예외 변수를 변경하고, 예외를 다시 발생시킵니다. 다시 throw되는 예외는 원래 예외이므로 생성된 출력은 다음과 같습니다.

Exception in F: G
Exception in Main: G

첫 번째 catch 블록이 현재 예외를 다시 throw하지 않고 e 예외를 throw했다면, 생성된 출력은 다음과 같습니다.

Exception in F: G
Exception in Main: F

예제 종료

break, continue, 또는 goto 문이 finally 블록 밖으로 컨트롤을 전송하는 것은 컴파일 시간 오류입니다. 블록 내에서 break, continue 또는 goto 문이 발생할 때, 해당 문의 대상은 동일한 finally 블록 내에 있어야 하며, 그렇지 않으면 컴파일 시 오류가 발생합니다.

return 문이 finally 블록에서 사용할 경우, 이는 컴파일 시간 오류입니다.

try 문에 실행이 도달하면, 제어권이 try 블록으로 전송됩니다. 예외가 전파되지 않고 컨트롤이 블록의 try 끝점에 도달하면 컨트롤이 블록으로 finally 전송됩니다(있는 경우). 블록이 finally 없으면 제어가 try 문의 끝점으로 전달됩니다.

예외가 전파된 경우, catch 절이 존재한다면, 어휘 순서대로 이를 검사하여 해당 예외와 일치하는 첫 번째 항목을 찾습니다. 일치하는 catch 조건문에 대한 검색은 §13.10.6에 설명된 대로 모든 외부 블록에서 계속 진행됩니다. catch 절은 예외 유형이 exception_specifier 중 하나에 일치하고 exception_filter가 true로 평가될 경우에 일치합니다. catch exception_specifier 없는 절은 예외 형식과 일치합니다. 예외 형식이 exception_specifier 또는 예외 형식의 기본 형식을 지정할 때 exception_specifier와 일치합니다. 절에 예외 필터가 포함된 경우 예외 개체가 예외 변수에 할당되고 예외 필터가 평가됩니다.

예외가 전파되고 일치하는 catch 절이 발견되면 컨트롤이 첫 번째 일치 catch 블록으로 전송됩니다. 예외가 전파되지 않고 컨트롤이 블록의 catch 끝점에 도달하면 컨트롤이 블록으로 finally 전송됩니다(있는 경우). 블록이 finally 없으면 제어가 try 문의 끝점으로 전달됩니다. catch 블록에서 예외가 전파된 경우, finally 블록이 존재하면 그쪽으로 제어가 전송됩니다. 예외는 다음으로 둘러싸고 있는 try 문으로 전파됩니다.

예외가 전파되고 일치하는 catch 절을 찾을 수 없는 경우 제어가 finally 블록으로 전송됩니다(있는 경우). 예외는 다음으로 둘러싸고 있는 try 문으로 전파됩니다.

finally 블록의 문은 제어가 try 문을 벗어날 때 항상 실행됩니다. 컨트롤 전송은 일반 실행의 결과로 발생하든, break, continue, goto, 또는 return 문을 실행한 결과로 발생하든, 혹은 try 문에서 예외 전파의 결과로 발생하든 상관없이 동일하게 적용됩니다. 예외가 전파되지 않고 컨트롤이 finally 블록의 끝점에 도달하면 제어가 문의 끝점으로 try 전송됩니다.

finally 블록을 실행하는 동안 예외가 발생하여 동일한 finally 블록 내에서 처리되지 않으면, 그 예외는 다음에 포함된 try 문으로 전파됩니다. 다른 예외가 전파되는 중이면 해당 예외가 손실됩니다. 예외를 전파하는 프로세스는 문 설명 throw (§13.10.6)에서 자세히 설명합니다.

예제: 다음 코드에서

public class Test
{
    static void Main()
    {
        try
        {
            Method();
        }
        catch (Exception ex) when (ExceptionFilter(ex))
        {
            Console.WriteLine("Catch");
        }

        bool ExceptionFilter(Exception ex)
        {
            Console.WriteLine("Filter");
            return true;
        }
    }

    static void Method()
    {
        try
        {
            throw new ArgumentException();
        }
        finally
        {
            Console.WriteLine("Finally");
        }
    }
}

메서드 Method 가 예외를 throw합니다. 첫 번째 작업은 catch를 실행하여 바깥쪽 절을 검사하는 것입니다. finally 절이 실행된 다음, 컨트롤이 바깥쪽 일치 Method 절로 전송되기 전에 catch 절이 실행됩니다. 결과 출력은 다음과 같습니다.

Filter
Finally
Catch

예제 종료

try 문 블록은 try 문이 도달 가능한 경우 도달할 수 있습니다.

catch 블록은 try 문에 도달할 수 있는 경우 try 문에 도달할 수 있습니다.

finally 문 블록은 try 문이 도달 가능한 경우 도달할 수 있습니다.

다음 두 가지가 모두 참이면 try 문의 끝점에 도달할 수 있습니다.

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

13.12 검사된 문장과 비검사된 문장

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

checked_statement
    : 'checked' block
    ;

unchecked_statement
    : 'unchecked' block
    ;

checked 문은 블록 내의 모든 식이 체크된 컨텍스트에서 계산되도록 하고, unchecked 문은 블록 내의 모든 식이 언체크된 컨텍스트에서 계산되도록 합니다.

checkedunchecked 문은 블록에서 작동한다는 점을 제외하고, checkedunchecked 연산자(§12.8.20)와 정확히 동일합니다.

13.13 lock 문장

이 문은 lock 지정된 개체에 대한 상호 배제 잠금을 가져오고 문을 실행한 다음 잠금을 해제합니다.

lock_statement
    : 'lock' '(' expression ')' embedded_statement
    ;

이 있는 문장은 lock로 알려진 형식의 값을 나타냅니다. 암시적 boxing 변환(§10.2.9)은 문장의 식lock에 대해 수행되지 않으므로, 식이 value_type 값을 나타낼 경우 컴파일 시간 오류가 발생합니다.

lock 형식의 문장입니다.

lock (x) ...

xreference_type의 표현일 때, 이는 다음과 정확히 동일합니다:

bool __lockWasTaken = false;
try
{
    System.Threading.Monitor.Enter(x, ref __lockWasTaken);
    ...
}
finally
{
    if (__lockWasTaken)
    {
        System.Threading.Monitor.Exit(x);
    }
}

단, x 한 번만 평가됩니다.

상호 배제 잠금이 유지되는 동안 동일한 실행 스레드에서 실행되는 코드는 잠금을 가져오고 해제할 수도 있습니다. 그러나 다른 스레드에서 실행되는 코드는 잠금이 해제될 때까지 잠금을 가져오지 못하도록 차단됩니다.

13.14 using 문장

이 문은 using 하나 이상의 리소스를 가져오고 문을 실행한 다음 리소스를 삭제합니다.

using_statement
    : 'using' '(' resource_acquisition ')' embedded_statement
    ;

resource_acquisition
    : local_variable_declaration
    | expression
    ;

리소스는 (System.IDisposable비동기 스트림의 경우) 인터페이스를 구현 IAsyncDisposable 하는 클래스 또는 구조체로, 비동기 스트림에 대해 명명된 DisposeDisposeAsync 단일 매개 변수 없는 메서드를 포함합니다. 리소스를 사용하는 코드는 리소스가 더 이상 필요하지 않음을 나타내기 위해 호출 Dispose 할 수 있습니다.

resource_acquisition의 형식이 local_variable_declaration인 경우, local_variable_declaration의 형식은 dynamic이거나 System.IDisposable으로 암시적으로 변환될 수 있는 형식이어야 합니다 (IAsyncDisposable은 비동기 스트림의 경우). resource_acquisition 형식이 인 경우, 이 식은 System.IDisposable으로(비동기 스트림에서는 IAsyncDisposable로) 암시적으로 변환 가능해야 합니다.

resource_acquisition 선언된 지역 변수는 읽기 전용이며 이니셜라이저를 포함해야 합니다. 포함된 문에서 할당 또는 연산자를 통해 이러한 지역 변수를 수정하거나, 해당 주소를 사용하거나 ++-- , 참조 또는 출력 매개 변수로 전달하려고 하면 컴파일 시간 오류가 발생합니다.

using 문은 인수, 사용 및 폐기로 번역됩니다. 리소스 사용은 try 절이 포함된 finally 문에서 암시적으로 이루어집니다. 이 finally 절은 리소스를 삭제합니다. 리소스를 null 획득하는 경우 Dispose 호출이 이루어지지 않으며(비DisposeAsync동기 스트림의 경우), 예외는 throw되지 않습니다. 리소스가 형식 dynamic 인 경우 사용 및 폐기 전에 변환이 성공하도록 하기 위해 인수 중에 암시적 동적 변환(§10.2.10) IDisposable 을 통해 (IAsyncDisposable 비동기 스트림의 경우) 동적으로 변환됩니다.

using 형식의 문장입니다.

using (ResourceType resource = «expression» ) «statement»

는 세 가지 가능한 확장 중 하나에 해당합니다. nullable이 아닌 값 형식 또는 값 형식 제약 조건(ResourceType)이 있는 형식 매개 변수인 경우 확장은 의미적으로 다음과 같습니다.

{
    ResourceType resource = «expression»;
    try
    {
        «statement»;
    }
    finally
    {
        ((IDisposable)resource).Dispose();
    }
}

resourceSystem.IDisposable로 형변환할 때 박싱이 발생하지 않는다는 점을 예외로 합니다.

그렇지 않은 경우 ResourceTypedynamic일 때 확장은 이렇게 됩니다.

{
    ResourceType resource = «expression»;
    IDisposable d = resource;
    try
    {
        «statement»;
    }
    finally
    {
        if (d != null)
        {
            d.Dispose();
        }
    }
}

그렇지 않으면 확장은

{
    ResourceType resource = «expression»;
    try
    {
        «statement»;
    }
    finally
    {
        IDisposable d = (IDisposable)resource;
        if (d != null)
        {
            d.Dispose();
        }
    }
}

어떤 확장에서든 resource 변수는 포함된 문에서 읽기 전용이고, d 변수는 포함된 문에 접근할 수 없으며 보이지 않습니다.

동작이 위의 확장과 일치하는 경우 성능상의 이유로 지정된 using_statement 다르게 구현할 수 있습니다.

using 형태의 문장:

using («expression») «statement»

동일한 세 가지 가능한 확장이 있습니다. 이 경우 ResourceType이 컴파일 시간 형식을 가지는 경우, 암시적으로 그 컴파일 시간 형식으로 사용됩니다. 그 외에는 인터페이스 IDisposable가 (IAsyncDisposable 비동기 스트림의 경우) 그대로 ResourceType로 사용됩니다. 변수는 resource 포함된 에 액세스할 수 없고 보이지 않습니다.

resource_acquisitionlocal_variable_declaration 형식을 사용하는 경우 지정된 형식의 여러 리소스를 획득할 수 있습니다. using 형식의 문장입니다.

using (ResourceType r1 = e1, r2 = e2, ..., rN = eN) «statement»

는 중첩된 using 문 시퀀스에 정확히 동일합니다.

using (ResourceType r1 = e1)
using (ResourceType r2 = e2)
...
using (ResourceType rN = eN)
«statement»

: 아래 예제에서는 log.txt 파일을 만들고 파일에 두 줄의 텍스트를 씁니다. 그런 다음 이 예제에서는 읽기 위해 동일한 파일을 열고 포함된 텍스트 줄을 콘솔에 복사합니다.

class Test
{
    static void Main()
    {
        using (TextWriter w = File.CreateText("log.txt"))
        {
            w.WriteLine("This is line one");
            w.WriteLine("This is line two");
        }
        using (TextReader r = File.OpenText("log.txt"))
        {
            string s;
            while ((s = r.ReadLine()) != null)
            {
                Console.WriteLine(s);
            }
        }
    }
}

TextWriterTextReader 클래스가 IDisposable 인터페이스를 구현하므로, 이 예제에서는 using 문을 사용하여 쓰기 또는 읽기 작업 후에 기본 파일이 제대로 닫히도록 할 수 있습니다.

예제 종료

13.15 수익률 문

이 문은 yield 반복기 블록(§13.3)에서 반복기의 열거자 개체(§15.15.5) 또는 열거 가능한 개체(§15.15.6)에 값을 생성하거나 반복의 끝을 알리는 데 사용됩니다.

yield_statement
    : 'yield' 'return' expression ';'
    | 'yield' 'break' ';'
    ;

yield는 상황별 키워드(§6.4.4)이며, return 또는 break 키워드 바로 앞에 사용될 때만 특별한 의미를 가집니다.

다음에 설명된 대로, yield 문이 나타날 수 있는 위치에는 몇 가지 제한이 있습니다.

  • yield, operator_body, 또는 accessor_body 외부에 나타나는 문(양식 중 하나)은 컴파일 시간 오류를 발생시킵니다.
  • 익명 함수 내에 yield 문(어떤 형식이든)이 나타나는 것은 컴파일 시간 오류입니다.
  • yield 절에 finally 문(어느 형식이든)이 나타나면 컴파일 시간 오류가 발생합니다 try 문에서.
  • yield return을 포함하는 try 문 내 어디에서나 문이 나타나는 것은 컴파일 타임 오류입니다.

예제: 다음 예제에서는 올바른 혹은 잘못된 명령문의 사용 예를 yield 보여 줍니다.

delegate IEnumerable<int> D();

IEnumerator<int> GetEnumerator()
{
    try
    {
        yield return 1; // Ok
        yield break;    // Ok
    }
    finally
    {
        yield return 2; // Error, yield in finally
        yield break;    // Error, yield in finally
    }
    try
    {
        yield return 3; // Error, yield return in try/catch
        yield break;    // Ok
    }
    catch
    {
        yield return 4; // Error, yield return in try/catch
        yield break;    // Ok
    }
    D d = delegate
    {
        yield return 5; // Error, yield in an anonymous function
    };
}

int MyMethod()
{
    yield return 1;     // Error, wrong return type for an iterator block
}

예제 종료

암시적 변환(§10.2)은 yield return 문에서 표현식의 형식과 반복자의 수확 형식(§15.15.4) 사이에 존재해야 합니다.

yield return 문은 다음과 같이 실행됩니다.

  • 문에 지정된 식은 계산되고 암시적으로 수율 형식으로 변환되며 열거자 개체의 속성에 Current 할당됩니다.
  • 반복기 블록의 실행이 일시 중단됩니다. yield return 문이 하나 이상의 try 블록 내에 있는 경우 연결된 finally 블록은 현재 실행되지 않습니다.
  • 열거자 개체의 메서드는 MoveNext 호출자로 반환 true 되며, 이는 열거자 개체가 다음 항목으로 성공적으로 진행되었음을 나타냅니다.

열거자 개체의 MoveNext 메서드에 대한 다음 호출은 마지막으로 일시 중단된 반복기 블록의 실행을 다시 시작합니다.

yield break 문은 다음과 같이 실행됩니다.

  • yield break 문이 하나 이상의 try 블록과 연결된 finally 블록으로 둘러싸여 있는 경우, 제어는 처음에 가장 안쪽 finally 문의 try 블록으로 전송됩니다. 컨트롤이 finally 블록의 끝점에 도달할 때, 컨트롤은 다음 바깥의 finally 문장의 try 블록으로 전송됩니다. 이 프로세스는 모든 바깥쪽 finally 문의 블록이 실행될 때까지 try 반복됩니다.
  • 컨트롤이 반복기 블록의 호출자에게 반환됩니다. MoveNext 열거자 개체의 메서드 또는 Dispose 메서드입니다.

yield break 문이 무조건 제어를 다른 곳으로 전송하기 때문에 yield break 문 끝점이 결코 도달하지 않습니다.