변수 9개
9.1 일반
변수는 스토리지 위치를 나타냅니다. 모든 변수에는 변수에 저장할 수 있는 값을 결정하는 형식이 있습니다. C#은 형식이 안전한 언어이며, C# 컴파일러는 변수에 저장된 값이 항상 적절한 형식임을 보장합니다. 변수 값은 할당을 통해 또는 및 ++
연산자를 --
사용하여 변경할 수 있습니다.
변수 값을 얻으려면 변수를 확실히 할당해야 합니다(§9.4).
다음 하위 항목에 설명된 대로 변수는 처음에 할당 되거나 처음에 할당되지 않습니다. 처음에 할당된 변수에는 잘 정의된 초기 값이 있으며 항상 확실히 할당된 것으로 간주됩니다. 처음에 할당되지 않은 변수에는 초기 값이 없습니다. 처음에 할당되지 않은 변수가 특정 위치에서 확실히 할당된 것으로 간주되려면 해당 위치로 이어지는 가능한 모든 실행 경로에서 변수에 대한 할당이 발생합니다.
9.2 변수 범주
9.2.1 일반
C#은 정적 변수, 인스턴스 변수, 배열 요소, 값 매개 변수, 입력 매개 변수, 참조 매개 변수, 출력 매개 변수 및 지역 변수의 8가지 범주를 정의합니다. 다음 하위클래스는 이러한 각 범주를 설명합니다.
예제: 다음 코드에서
class A { public static int x; int y; void F(int[] v, int a, ref int b, out int c, in int d) { int i = 1; c = a + b++ + d; } }
x
는 정적 변수이고,y
인스턴스 변수이고,v[0]
배열 요소이고,a
값 매개 변수이고,b
참조 매개 변수이고,c
출력 매개 변수이고,d
입력 매개 변수이며i
, 지역 변수입니다. 끝 예제
9.2.2 정적 변수
한정자를 사용하여 static
선언된 필드는 정적 변수입니다. 정적 변수는 포함하는 형식에 static
대한 생성자(§15.12)를 실행하기 전에 존재하게 되며, 연결된 애플리케이션 도메인이 존재하지 않을 때 존재하지 않습니다.
정적 변수의 초기 값은 변수 형식의 기본값(§9.3)입니다.
명확한 할당 검사를 위해 정적 변수는 처음에 할당된 것으로 간주됩니다.
9.2.3 인스턴스 변수
9.2.3.1 일반
한정자 없이 static
선언된 필드는 인스턴스 변수입니다.
9.2.3.2 클래스의 인스턴스 변수
클래스의 인스턴스 변수는 해당 클래스의 새 인스턴스가 만들어지면 존재하게 되며, 해당 인스턴스에 대한 참조가 없고 인스턴스의 종료자(있는 경우)가 실행되면 존재하지 않습니다.
클래스의 인스턴스 변수의 초기 값은 변수 형식의 기본값(§9.3)입니다.
명확한 할당 검사를 위해 클래스의 인스턴스 변수가 처음에 할당된 것으로 간주됩니다.
9.2.3.3 구조체의 인스턴스 변수
구조체의 인스턴스 변수는 구조체 변수가 속한 구조체 변수와 정확히 동일한 수명을 줍니다. 즉, 구조체 형식의 변수가 존재하거나 존재하지 않을 때 구조체의 인스턴스 변수도 존재합니다.
구조체의 인스턴스 변수의 초기 할당 상태는 포함하는 struct
변수의 할당 상태와 동일합니다. 즉, 구조체 변수가 처음에 할당된 것으로 간주될 때 해당 인스턴스 변수도 마찬가지이며 구조체 변수가 처음에 할당되지 않은 것으로 간주될 때 해당 인스턴스 변수도 마찬가지로 할당되지 않습니다.
9.2.4 배열 요소
배열의 요소는 배열 인스턴스를 만들 때 존재하게 되며 해당 배열 인스턴스에 대한 참조가 없으면 존재하지 않습니다.
배열의 각 요소의 초기 값은 배열 요소 형식의 기본값(§9.3)입니다.
명확한 할당 검사를 위해 배열 요소는 처음에 할당된 것으로 간주됩니다.
9.2.5 값 매개 변수
값 매개 변수는 매개 변수가 속한 함수 멤버(메서드, 인스턴스 생성자, 접근자 또는 연산자) 또는 익명 함수를 호출할 때 존재하며 호출에 지정된 인수의 값으로 초기화됩니다. 일반적으로 값 매개 변수는 함수 본문의 실행이 완료되면 존재하지 않습니다. 그러나 값 매개 변수가 익명 함수(§12.19.6.2)에 의해 캡처되는 경우 해당 익명 함수에서 만든 대리자 또는 식 트리가 가비지 수집에 적합할 때까지 해당 수명이 적어도 연장됩니다.
명확한 할당 검사를 위해 값 매개 변수는 처음에 할당된 것으로 간주됩니다.
값 매개 변수는 §15.6.2.2에서 자세히 설명합니다.
9.2.6 참조 매개 변수
참조 매개 변수는 함수 멤버, 대리자, 익명 함수 또는 로컬 함수를 호출할 때 존재하게 되는 참조 변수(§9.7)이며 해당 참조는 해당 호출에서 인수로 지정된 변수로 초기화됩니다. 함수 본문 실행이 완료되면 참조 매개 변수가 더 이상 존재하지 않습니다. 값 매개 변수와 달리 참조 매개 변수는 캡처되지 않습니다(§9.7.2.9).
참조 매개 변수에는 다음과 같은 명확한 할당 규칙이 적용됩니다.
참고: 출력 매개 변수에 대한 규칙은 다르며 (§9.2.7)에 설명되어 있습니다. 끝 메모
- 변수는 함수 멤버 또는 대리자 호출에서 참조 매개 변수로 전달되기 전에 확실히 할당되어야 합니다(§9.4).
- 함수 멤버 또는 익명 함수 내에서 참조 매개 변수는 처음에 할당된 것으로 간주됩니다.
참조 매개 변수는 §15.6.2.3.3에서 자세히 설명합니다.
9.2.7 출력 매개 변수
출력 매개 변수는 함수 멤버, 대리자, 익명 함수 또는 로컬 함수를 호출할 때 존재하게 되는 참조 변수(§9.7)이며 해당 참조는 해당 호출에서 인수로 지정된 변수로 초기화됩니다. 함수 본문 실행이 완료되면 출력 매개 변수가 존재하지 않습니다. 값 매개 변수와 달리 출력 매개 변수는 캡처되지 않습니다(§9.7.2.9).
출력 매개 변수에는 다음과 같은 명확한 할당 규칙이 적용됩니다.
참고: 참조 매개 변수에 대한 규칙은 다르며 (§9.2.6)에 설명되어 있습니다. 끝 메모
- 변수를 함수 멤버 또는 대리자 호출에서 출력 매개 변수로 전달하기 전에 변수를 확실히 할당할 필요가 없습니다.
- 함수 멤버 또는 대리자 호출이 정상적으로 완료된 후 출력 매개 변수로 전달된 각 변수는 해당 실행 경로에 할당된 것으로 간주됩니다.
- 함수 멤버 또는 익명 함수 내에서 출력 매개 변수는 처음에 할당되지 않은 것으로 간주됩니다.
- 함수 멤버, 무명 함수 또는 로컬 함수의 모든 출력 매개 변수는 함수 멤버, 무명 함수 또는 로컬 함수가 정상적으로 반환되기 전에 확실히 할당되어야 합니다(§9.4).
출력 매개 변수는 §15.6.2.3.4에서 자세히 설명합니다.
9.2.8 입력 매개 변수
입력 매개 변수는 함수 멤버, 대리자, 익명 함수 또는 로컬 함수를 호출할 때 존재하는 참조 변수(§9.7)이며 해당 참조는 해당 호출에서 인수로 지정된 variable_reference 초기화됩니다. 함수 본문 실행이 완료되면 입력 매개 변수가 존재하지 않습니다. 값 매개 변수와 달리 입력 매개 변수는 캡처되지 않습니다(§9.7.2.9).
입력 매개 변수에는 다음과 같은 명확한 할당 규칙이 적용됩니다.
- 변수는 함수 멤버 또는 대리자 호출에서 입력 매개 변수로 전달되기 전에 확실히 할당되어야 합니다(§9.4).
- 함수 멤버, 익명 함수 또는 로컬 함수 내에서 입력 매개 변수는 처음에 할당된 것으로 간주됩니다.
입력 매개 변수는 §15.6.2.3.2에서 자세히 설명합니다.
9.2.9 지역 변수
9.2.9.1 일반
지역 변수는 try_statement local_variable_declaration, declaration_expression, foreach_statement 또는 specific_catch_clause 의해 선언됩니다. 특정 종류의 패턴 s(§11)에서 지역 변수를 선언할 수도 있습니다. foreach_statement 경우 지역 변수는 반복 변수(§13.9.5)입니다. specific_catch_clause 경우 지역 변수는 예외 변수(§13.11)입니다. foreach_statement 또는 specific_catch_clause 선언된 지역 변수는 처음에 할당된 것으로 간주됩니다.
블록, for_statement, switch_block 또는 using_statement local_variable_declaration 발생할 수 있습니다.
declaration_expression argument_valueout
(§12.21.2)로 발생할 수 있습니다.
지역 변수의 수명은 스토리지가 예약되도록 보장하는 동안 프로그램 실행의 부분입니다. 이 수명은 적어도 해당 범위의 실행이 어떤 식으로든 끝날 때까지 해당 범위가 연결된 범위로의 항목에서 확장됩니다. (묶 은 블록을 입력하거나, 메서드를 호출하거나, 반복기 블록에서 값을 생성하면 현재 범위의 실행이 일시 중단되지만 종료되지는 않습니다.) 로컬 변수가 익명 함수(§12.19.6.2)에 의해 캡처되는 경우 해당 수명은 적어도 익명 함수에서 만든 대리자 또는 식 트리와 캡처된 변수를 참조하는 다른 개체가 가비지 수집에 적합할 때까지 연장됩니다. 부모 범위가 재귀적으로 또는 반복적으로 입력되면 매번 지역 변수의 새 인스턴스가 생성되고, 이니셜라이저(있는 경우)가 매번 평가됩니다.
참고: 지역 변수는 범위가 입력될 때마다 인스턴스화됩니다. 이 동작은 익명 메서드를 포함하는 사용자 코드에 표시됩니다. 끝 메모
참고: foreach_statement선언된 반복 변수(§13.9.5)의 수명은 해당 문의 단일 반복입니다. 각 반복은 새 변수를 만듭니다. 끝 메모
참고: 지역 변수의 실제 수명은 구현에 따라 달라집니다. 예를 들어 컴파일러는 블록의 로컬 변수가 해당 블록의 작은 부분에만 사용되는지 정적으로 확인할 수 있습니다. 이 분석을 사용하여 컴파일러는 변수의 스토리지 수명이 포함하는 블록보다 짧은 코드를 생성할 수 있습니다.
로컬 참조 변수에서 참조하는 스토리지는 해당 로컬 참조 변수(§7.9)의 수명과 독립적으로 회수됩니다.
끝 메모
local_variable_declaration 또는 declaration_expression 도입된 지역 변수는 자동으로 초기화되지 않으므로 기본값이 없습니다. 이러한 지역 변수는 처음에 할당되지 않은 것으로 간주됩니다.
참고: 이 니셜라이저를 포함하는 local_variable_declaration 아직 초기 할당되지 않았습니다. 선언 실행은 변수에 대한 할당과 똑같이 동작합니다(§9.4.4.5). 이니셜라이저가 실행되기 전에 변수 사용 예를 들어 이니셜라이저 식 자체 내에서 또는 이니셜라이저를 우회하는 goto_statement 사용하여 컴파일 시간 오류입니다.
goto L; int x = 1; // never executed L: x += 1; // error: x not definitely assigned
지역 변수의 범위 내에서 선언자 앞에 있는 텍스트 위치에서 해당 지역 변수를 참조하는 것은 컴파일 시간 오류입니다.
끝 메모
9.2.9.2 폐기
무시는 이름이 없는 지역 변수입니다. 무시는 식별자가 있는 선언 식(_
)에 의해 도입되며 암시적으로 형식화되거나 명시적으로_
var _
형식화(T _
)됩니다.
참고:
_
여러 형식의 선언에서 유효한 식별자입니다. 끝 메모
무시에는 이름이 없으므로 해당 변수가 나타내는 변수에 대한 유일한 참조는 해당 변수를 소개하는 식입니다.
참고: 삭제는 출력 인수로 전달될 수 있으므로 해당 출력 매개 변수가 연결된 스토리지 위치를 나타낼 수 있습니다. 끝 메모
삭제는 처음에 할당되지 않으므로 항상 해당 값에 액세스하는 것은 오류입니다.
예제:
_ = "Hello".Length; (int, int, int) M(out int i1, out int i2, out int i3) { ... } (int _, var _, _) = M(out int _, out var _, out _);
이 예제에서는 범위에 이름
_
선언이 없다고 가정합니다.식의 결과를 무시하기
_
위한 간단한 패턴을 보여 주는 할당입니다. 호출M
은 튜플 및 출력 매개 변수로 사용할 수 있는 다양한 형태의 삭제를 보여 줍니다.끝 예제
9.3 기본값
변수의 다음 범주는 기본값으로 자동으로 초기화됩니다.
- 정적 변수
- 클래스 인스턴스의 인스턴스 변수입니다.
- 배열 요소.
변수의 기본값은 변수의 형식에 따라 달라지며 다음과 같이 결정됩니다.
- value_type 변수의 경우 기본값은 value_type 기본 생성자(§8.3.3)에서 계산한 값과 동일합니다.
- reference_type 변수의
null
참고: 기본값으로의 초기화는 일반적으로 메모리 관리자 또는 가비지 수집기가 메모리를 사용하기 위해 할당되기 전에 all-bits-zero로 초기화하도록 하여 수행됩니다. 따라서 all-bits-zero를 사용하여 null 참조를 나타내는 것이 편리합니다. 끝 메모
9.4 명확한 과제
9.4.1 일반
함수 멤버 또는 무명 함수의 실행 코드에서 지정된 위치에서 변수는 특정 정적 흐름 분석(
참고: 비공식적으로 명시된 명확한 할당 규칙은 다음과 같습니다.
- 처음에 할당된 변수(§9.4.2)는 항상 확실히 할당된 것으로 간주됩니다.
- 처음에 할당되지 않은 변수(§9.4.3)는 해당 위치로 이어지는 모든 가능한 실행 경로에 다음 중 하나 이상이 포함된 경우 지정된 위치에 확실히 할당된 것으로 간주됩니다.
- 변수가 왼쪽 피연산자인 단순 할당(§12.21.2)입니다.
- 변수를 출력 매개 변수로 전달하는 호출 식(§12.8.10) 또는 개체 생성 식(§12.8.17.2)입니다.
- 지역 변수의 경우 변수 이니셜라이저를 포함하는 변수(§13.6.2)에 대한 지역 변수 선언입니다.
위의 비공식 규칙의 기초가 되는 공식 사양은 §9.4.2, §9.4.3 및 §9.4.4에 설명되어 있습니다.
끝 메모
struct_type 변수의 인스턴스 변수에 대한 명확한 할당 상태는 개별적으로 그리고 전체적으로 추적됩니다. §9.4.2, §9.4.3 및 §9.4.4에 설명된 규칙 외에도 struct_type 변수 및 해당 인스턴스 변수에 다음 규칙이 적용됩니다.
- 포함된 struct_type 변수가 확실히 할당된 것으로 간주되는 경우 인스턴스 변수는 확실히 할당된 것으로 간주됩니다.
- 각 인스턴스 변수가 확실히 할당된 것으로 간주되는 경우 struct_type 변수는 확실히 할당된 것으로 간주됩니다.
명확한 할당은 다음 컨텍스트에서 요구 사항입니다.
변수는 값을 가져오는 각 위치에 확실히 할당되어야 합니다.
참고: 이렇게 하면 정의되지 않은 값이 발생하지 않습니다. 끝 메모
식에서 변수의 발생은 다음을 제외하고 변수 값을 가져오는 것으로 간주됩니다.
- 변수는 단순 할당의 왼쪽 피연산자입니다.
- 변수가 출력 매개 변수로 전달되거나
- 변수는 struct_type 변수이며 멤버 액세스의 왼쪽 피연산자로 발생합니다.
변수는 참조 매개 변수로 전달되는 각 위치에 확실히 할당되어야 합니다.
참고: 이렇게 하면 호출되는 함수 멤버가 처음에 할당된 참조 매개 변수를 고려할 수 있습니다. 끝 메모
변수는 입력 매개 변수로 전달되는 각 위치에 확실히 할당되어야 합니다.
참고: 이렇게 하면 호출되는 함수 멤버가 처음에 할당된 입력 매개 변수를 고려할 수 있습니다. 끝 메모
함수 멤버의 모든 출력 매개 변수는 함수 멤버가 반환하는 각 위치(return 문을 통해 또는 함수 멤버 본문의 끝에 도달하는 실행을 통해)에 확실히 할당되어야 합니다.
참고: 이렇게 하면 함수 멤버가 출력 매개 변수에 정의되지 않은 값을 반환하지 않으므로 컴파일러에서 변수를 변수에 대한 할당에 해당하는 출력 매개 변수로 사용하는 함수 멤버 호출을 고려할 수 있습니다. 끝 메모
struct_type
this
는 해당 인스턴스 생성자가 반환하는 각 위치에 확실히 할당되어야 합니다.
9.4.2 처음에 할당된 변수
변수의 다음 범주는 처음에 할당된 것으로 분류됩니다.
- 정적 변수
- 클래스 인스턴스의 인스턴스 변수입니다.
- 처음에 할당된 구조체 변수의 인스턴스 변수입니다.
- 배열 요소.
- 값 매개 변수입니다.
- 참조 매개 변수입니다.
- 입력 매개 변수.
- 절 또는 문에
catch
선언된 변수입니다foreach
.
9.4.3 처음에 할당되지 않은 변수
변수의 다음 범주는 처음에 할당되지 않은 것으로 분류됩니다.
- 처음에 할당되지 않은 구조체 변수의 인스턴스 변수입니다.
- 생성자 이니셜라이저가
this
없는 구조체 인스턴스 생성자의 변수를 포함한 출력 매개 변수입니다. - 절 또는 문에 선언된 변수를
catch
제외한 지역 변수입니다foreach
.
9.4.4 명확한 할당을 결정하기 위한 정확한 규칙
9.4.4.1 일반
사용된 각 변수가 확실히 할당되었는지 확인하기 위해 컴파일러는 이 하위 클래스에 설명된 것과 동일한 프로세스를 사용해야 합니다.
함수 멤버의 본문은 처음에 할당되지 않은 변수를 하나 이상 선언할 수 있습니다. 처음에 할당되지 않은 각 변수 v대해 컴파일러는 함수 멤버의 다음 각 지점에서 v 대한 명확한 할당 상태 결정해야 합니다.
- 각 문의 시작 부분에서
- 각 문의 끝점(§13.2)에서
- 제어를 다른 문이나 문의 끝점으로 전송하는 각 호에서
- 각 식의 시작 부분에서
- 각 식의 끝에
v의 명확한 할당 상태는 다음 중 하나일 수 있습니다.
- 확실히 할당. 이는 이 시점 까지 가능한 모든 제어 흐름에서 v 에 값이 할당되었음을 나타냅니다.
- 확실히 할당되지 않았습니다. 형식
bool
식의 끝에 있는 변수의 상태에 대해 확실히 할당되지 않은 변수의 상태는 다음 하위 상태 중 하나에 속할 수 있습니다(반드시 그런 것은 아님).- true 식 후에 확실히 할당됩니다. 이 상태는 부울 식이 true로 평가될 경우 v 가 확실히 할당되지만 부울 식이 false로 평가될 경우 반드시 할당되지는 않음을 나타냅니다.
- false 식 후에 확실히 할당됩니다. 이 상태는 부울 식이 false로 평가될 경우 v 가 확실히 할당되지만 부울 식이 true로 평가되는 경우 반드시 할당되지는 않음을 나타냅니다.
다음 규칙은 각 위치에서 변수 v 의 상태를 결정하는 방법을 제어합니다.
9.4.4.2 문에 대한 일반 규칙
- v 는 함수 멤버 본문의 시작 부분에 확실히 할당되지 않습니다.
- 다른 문의 시작 부분에 있는 v의 명확한 할당 상태는 해당 문의 시작을 대상으로 하는 모든 제어 흐름 전송에서 v의 명확한 할당 상태를 확인하여 결정됩니다. v가 이러한 모든 제어 흐름 전송에 확실히 할당된 경우에만 v는 문의 시작 부분에 확실히 할당됩니다. 가능한 제어 흐름 전송 집합은 문 연결 가능성 확인(§13.2)과 동일한 방식으로 결정됩니다.
- ,, ,, ,
block
checked
unchecked
if
while
do
for
foreach
, 또는lock
문의 끝점에 있는using
vswitch
명확한 할당 상태는 해당 문의 끝점을 대상으로 하는 모든 제어 흐름 전송에서 v의 명확한 할당 상태를 확인하여 결정됩니다. v가 이러한 모든 제어 흐름 전송에 확실히 할당된 경우 v는 문의 끝점에 확실히 할당됩니다. 그렇지 않으면 v 는 문의 끝점에 확실히 할당되지 않습니다. 가능한 제어 흐름 전송 집합은 문 연결 가능성 확인(§13.2)과 동일한 방식으로 결정됩니다.
참고: 연결할 수 없는 문 에 대한 제어 경로가 없으므로 연결할 수 없는 문의 시작 부분에 v 가 확실히 할당됩니다. 끝 메모
9.4.4.3 블록 문, 선택됨 및 선택되지 않은 문
제어에서 v의 명확한 할당 상태는 블록에 있는 문 목록의 첫 번째 문(또는 문 목록이 비어 있는 경우 블록의 끝점)으로 전송됩니다. 블록 또는 checked
문 앞에 있는 vunchecked
명확한 할당 문과 동일합니다.
9.4.4.4 식 문
expr 식으로 구성된 식 문 stmt의 경우:
- v는 stmt의 시작 부분과 expr의 시작 부분에서 동일한 명확한 할당 상태를 가짐
- v가 expr의 끝에 확실히 할당된 경우 stmt의 끝점에 확실히 할당됩니다. 그렇지 않으면 stmt의 끝점에 확실히 할당되지 않습니다.
9.4.4.5 선언문
- stmt가 이니셜라이저가 없는 선언문인 경우 v는 stmt의 시작 부분과 동일한 stmt 끝점에서 명확한 할당 상태를 줍니다.
- stmt가 이니셜라이저가 있는 선언문인 경우 v에 대한 명확한 할당 상태는 stmt가 문 목록인 것처럼 결정되며, 선언 순서대로 이니셜라이저가 있는 각 선언에 대해 하나의 할당 문이 있습니다.
9.4.4.6 If 문
폼의 문 stmt 의 경우:
if ( «expr» ) «then_stmt» else «else_stmt»
- v는 stmt의 시작 부분과 expr의 시작 부분에서 동일한 명확한 할당 상태를 가짐
- v가 expr의 끝에 확실히 할당된 경우 제어 흐름 전송 시 then_stmt 및 else_stmt 또는 stmt의 끝점(다른 절이 없는 경우)에 확실히 할당됩니다.
- v가 expr의 끝에 "true 식 후에 확실히 할당됨" 상태가 있는 경우 제어 흐름이 then_stmt 전송에 확실히 할당되며, 다른 절이 없는 경우 else_stmt 또는 stmt의 끝 지점으로 제어 흐름 전송에 확실히 할당되지 않습니다.
- v가 expr의 끝에 "false 식 후에 확실히 할당됨" 상태가 있는 경우 else_stmt 제어 흐름 전송에 확실히 할당되며 then_stmt 제어 흐름 전송에 확실히 할당되지 않습니다. then_stmt 끝점에 확실히 할당된 경우에만 stmt의 끝점에 확실히 할당됩니다.
- 그렇지 않으면 v는 제어 흐름이 then_stmt 또는 else_stmt 전송되거나 다른 절이 없는 경우 stmt의 끝점에 확실히 할당되지 않은 것으로 간주됩니다.
9.4.4.7 Switch 문
switch
제어 식 expr이 있는 문 stmt의 경우:
expr의 시작 부분에 있는 v의 명확한 할당 상태는 stmt의 시작 부분에 있는 v의 상태와 동일합니다.
케이스의 가드 절 시작 부분에 있는 v 의 명확한 할당 상태는 다음과 입니다.
- v가 switch_label 선언된 패턴 변수인 경우: "확실히 할당됨".
- 해당 guard 절(§13.8.3)을 포함하는 스위치 레이블에 연결할 수 없는 경우: "확실히 할당됨".
- 그렇지 않으면 v의 상태는 expr 이후의 v 상태와 동일합니다.
예제: 두 번째 규칙은 할당되지 않은 변수를 도달할 수 없는 코드에서 액세스하는 경우 컴파일러가 오류를 표시할 필요가 없습니다. b의 상태는 연결할 수 없는 스위치 레이블
case 2 when b
에서 "확실히 할당"됩니다.bool b; switch (1) { case 2 when b: // b is definitely assigned here. break; }
끝 예제
제어 흐름에서 연결 가능한 switch 블록 문 목록으로 전송되는 v의 명확한 할당 상태는 다음과 입니다.
- 제어 전송이 'goto case' 또는 'goto default' 문 때문인 경우 v의 상태는 해당 'goto' 문의 시작 부분에 있는 상태와 같습니다.
- 컨트롤 전송이 스위치의 레이블 때문
default
인 경우 v의 상태는 expr 이후의 v 상태와 동일합니다. - 컨트롤 전송이 연결할 수 없는 스위치 레이블로 인해 발생하는 경우 v의 상태는 "확실히 할당"됩니다.
- 제어 전송이 가드 절이 있는 연결 가능한 스위치 레이블 때문인 경우 v의 상태는 guard 절 뒤의 v 상태와 동일합니다.
- 제어 전송이 가드 절이 없는 연결 가능한 스위치 레이블로 인해 발생하는 경우 v의 상태는 다음과 입니다.
- v가 switch_label 선언된 패턴 변수인 경우: "확실히 할당됨".
- 그렇지 않으면 v의 상태는 expr 이후 v의 통계와 동일합니다.
이러한 규칙의 결과는 switch_label 선언된 패턴 변수가 해당 섹션의 유일한 연결 가능한 스위치 레이블이 아닌 경우 스위치 섹션의 문에 "확실히 할당되지 않음"입니다.
예제:
public static double ComputeArea(object shape) { switch (shape) { case Square s when s.Side == 0: case Circle c when c.Radius == 0: case Triangle t when t.Base == 0 || t.Height == 0: case Rectangle r when r.Length == 0 || r.Height == 0: // none of s, c, t, or r is definitely assigned return 0; case Square s: // s is definitely assigned return s.Side * s.Side; case Circle c: // c is definitely assigned return c.Radius * c.Radius * Math.PI; … } }
끝 예제
9.4.4.8 While 문
폼의 문 stmt 의 경우:
while ( «expr» ) «while_body»
- v는 stmt의 시작 부분과 expr의 시작 부분에서 동일한 명확한 할당 상태를 가짐
- v가 expr의 끝에 확실히 할당된 경우 while_body 및 stmt의 끝점으로 제어 흐름 전송에 확실히 할당됩니다.
- v가 expr의 끝에 "true 식 후에 확실히 할당됨" 상태가 있는 경우 while_body 제어 흐름 전송에 확실히 할당되지만 stmt의 끝점에는 확실히 할당되지 않습니다.
- v가 expr의 끝에 "false 식 후에 확실히 할당됨" 상태가 있는 경우 stmt의 끝점으로 제어 흐름 전송에 확실히 할당되지만 while_body 제어 흐름 전송에는 확실히 할당되지 않습니다.
9.4.4.9 Do 문
폼의 문 stmt 의 경우:
do «do_body» while ( «expr» ) ;
- v는 stmt의 시작 부분에서 do_body 제어 흐름 전송에 대한 명확한 할당 상태가 stmt의 시작 부분과 동일합니다.
- v는 expr의 시작 부분에서 do_body 끝점과 동일한 명확한 할당 상태를 가짐
- v가 expr의 끝에 확실히 할당된 경우 stmt의 끝점으로 제어 흐름 전송에 확실히 할당됩니다.
- v가 expr의 끝에 "false 식 후에 확실히 할당됨" 상태가 있는 경우 stmt의 끝점으로 제어 흐름 전송에 확실히 할당되지만 do_body 제어 흐름 전송에는 확실히 할당되지 않습니다.
9.4.4.10 For 문
양식의 문은 다음과 같습니다.
for ( «for_initializer» ; «for_condition» ; «for_iterator» )
«embedded_statement»
명확한 할당 검사는 문이 작성된 것처럼 수행됩니다.
{
«for_initializer» ;
while ( «for_condition» )
{
«embedded_statement» ;
LLoop: «for_iterator» ;
}
}
에는 continue
레이블for
을 대상으로 하는 문으로 goto
변환되는 문을 대상으로 LLoop
하는 문이 있습니다.
for_condition 문에서 생략하면 위의 확장에서 for
for_condition true로 대체된 것처럼 명확한 할당 평가가 진행됩니다.
9.4.4.11 Break, continue 및 goto 문
또는 문으로 인한 break
continue
제어 흐름 전송에 대한 vgoto
명확한 할당 상태는 문의 시작 부분에 있는 v의 명확한 할당 상태와 동일합니다.
9.4.4.12 Throw 문
폼의 문 stmt 의 경우:
throw «expr» ;
expr의 시작 부분에 있는 v의 명확한 할당 상태는 stmt의 시작 부분에 있는 v의 명확한 할당 상태와 동일합니다.
9.4.4.13 Return 문
폼의 문 stmt 의 경우:
return «expr» ;
- expr의 시작 부분에 있는 v의 명확한 할당 상태는 stmt의 시작 부분에 있는 v의 명확한 할당 상태와 동일합니다.
- v가 출력 매개 변수인 경우 다음 중 하나를 확실히 할당해야 합니다.
- after expr
- 또는 문을 묶는
finally
try
-finally
try
또는 해당 블록의-catch
-끝에finally
있습니다.return
폼의 문 stmt 의 경우:
return ;
- v가 출력 매개 변수인 경우 다음 중 하나를 확실히 할당해야 합니다.
- before stmt
- 또는 문을 묶는
finally
try
-finally
try
또는 해당 블록의-catch
-끝에finally
있습니다.return
9.4.4.14 Try-catch 문
폼의 문 stmt 의 경우:
try «try_block»
catch ( ... ) «catch_block_1»
...
catch ( ... ) «catch_block_n»
- try_block 시작 부분에 있는 v의 명확한 할당 상태는 stmt의 시작 부분에 있는 v의 명확한 할당 상태와 동일합니다.
- catch_block_i 시작 부분에 있는 v의 명확한 할당 상태는 stmt의 시작 부분에 있는 v의 명확한 할당 상태와 동일합니다.
- stmt의 끝점에서 v의 명확한 할당 상태는 v가 try_block 끝점과 모든 catch_block_i(1에서 n까지의 모든 i에 대해) 확실히 할당된 경우에만 할당됩니다.
9.4.4.15 Try-finally 문
폼의 문 stmt 의 경우:
try «try_block» finally «finally_block»
- try_block 시작 부분에 있는 v의 명확한 할당 상태는 stmt의 시작 부분에 있는 v의 명확한 할당 상태와 동일합니다.
- finally_block 시작 부분에 있는 v의 명확한 할당 상태는 stmt의 시작 부분에 있는 v의 명확한 할당 상태와 동일합니다.
- 다음 중 하나 이상이 true인 경우에만 stmt의 끝점에 있는 v의 명확한 할당 상태가 확실히 할당됩니다.
- v 는 확실히 try_block 끝점에 할당됩니다.
- v 는 확실히 finally_block 엔드포인트 에 할당됩니다.
제어 흐름 전송(예: goto
문)이 try_block 내에서 시작되고 try_block 외부에서 끝나는 경우 v가 finally_block 끝점에 확실히 할당된 경우 해당 제어 흐름 전송에 확실히 할당된 것으로 간주됩니다. (v가 이 제어 흐름 전송에 대해 다른 이유로 확실히 할당된 경우 여전히 확실히 할당된 것으로 간주되는 경우에만 해당되지 않습니다.)
9.4.4.16 Try-catch-finally 문
양식의 문은 다음과 같습니다.
try «try_block»
catch ( ... ) «catch_block_1»
...
catch ( ... ) «catch_block_n»
finally «finally_block»
명확한 할당 분석은 문이 문을 둘러싸try
-finally
는 문인 try
-catch
것처럼 수행됩니다.
try
{
try «try_block»
catch ( ... ) «catch_block_1»
...
catch ( ... ) «catch_block_n»
}
finally «finally_block»
예제: 다음 예제에서는 문의 여러 블록
try
(§13.11)이 명확한 할당에 미치는 영향을 보여 줍니다.class A { static void F() { int i, j; try { goto LABEL; // neither i nor j definitely assigned i = 1; // i definitely assigned } catch { // neither i nor j definitely assigned i = 3; // i definitely assigned } finally { // neither i nor j definitely assigned j = 5; // j definitely assigned } // i and j definitely assigned LABEL: ; // j definitely assigned } }
끝 예제
9.4.4.17 Foreach 문
폼의 문 stmt 의 경우:
foreach ( «type» «identifier» in «expr» ) «embedded_statement»
- expr의 시작 부분에 있는 v의 명확한 할당 상태는 stmt의 시작 부분에 있는 v의 상태와 동일합니다.
- 제어 흐름에서 embedded_statement 또는 stmt의 끝점으로 전송되는 v의 명확한 할당 상태는 expr의 끝에 있는 v의 상태와 동일합니다.
9.4.4.18 Using 문
폼의 문 stmt 의 경우:
using ( «resource_acquisition» ) «embedded_statement»
- resource_acquisition 시작 부분에 있는 v의 명확한 할당 상태는 stmt의 시작 부분에 있는 v의 상태와 동일합니다.
- 제어 흐름에서 embedded_statement 전송되는 v의 명확한 할당 상태는 resource_acquisition 끝에 있는 v의 상태와 동일합니다.
9.4.4.19 Lock 문
폼의 문 stmt 의 경우:
lock ( «expr» ) «embedded_statement»
- expr의 시작 부분에 있는 v의 명확한 할당 상태는 stmt의 시작 부분에 있는 v의 상태와 동일합니다.
- embedded_statement 제어 흐름 전송 시 v의 명확한 할당 상태는 expr 종료 시 v의 상태와 동일합니다.
9.4.4.20 Yield 문
폼의 문 stmt 의 경우:
yield return «expr» ;
- expr의 시작 부분에 있는 v의 명확한 할당 상태는 stmt의 시작 부분에 있는 v의 상태와 동일합니다.
- stmt의 끝에 있는 v의 명확한 할당 상태는 expr의 끝에 있는 v의 상태와 동일합니다.
yield break
문은 명확한 할당 상태에 영향을 주지 않습니다.
9.4.4.21 상수 식에 대한 일반 규칙
다음은 상수 식에 적용되며 적용할 수 있는 다음 섹션의 규칙보다 우선합니다.
값 true
이 있는 상수 식의 경우:
- v가 식 앞에 확실히 할당된 경우 v는 식 다음에 확실히 할당됩니다.
- 그렇지 않으면 v 는 식 후 "false 식 후에 확실히 할당"됩니다.
예제:
int x; if (true) {} else { Console.WriteLine(x); }
끝 예제
값 false
이 있는 상수 식의 경우:
- v가 식 앞에 확실히 할당된 경우 v는 식 다음에 확실히 할당됩니다.
- 그렇지 않으면 v 는 식 후 "true 식 후에 확실히 할당"됩니다.
예제:
int x; if (false) { Console.WriteLine(x); }
끝 예제
다른 모든 상수 식의 경우 식 뒤의 v의 명확한 할당 상태는 식 앞의 v의 명확한 할당 상태와 동일합니다.
9.4.4.22 단순 식에 대한 일반 규칙
다음 규칙은 리터럴(§12.8.2), 단순 이름(§12.8.4), 멤버 액세스 식(§12.8.7), 인덱싱되지 않은 기본 액세스 식(§12.8.15), typeof
식(§12.8.18), 기본값 식(§12.8.21), nameof
식(§12.8.23) 및 선언 식(§12.17)을 적용합니다.
- 이러한 식의 끝에 있는 v의 명확한 할당 상태는 식의 시작 부분에 있는 v의 명확한 할당 상태와 동일합니다.
9.4.4.23 포함된 식이 있는 식에 대한 일반 규칙
괄호 처리된 식(§12.8.5), 튜플 식(§12.8.6), 요소 액세스 식(§12.8.12), 인덱싱이 있는 기본 액세스 식(§12.8.15), 증가 및 감소 식(§12.8.16, §12.9.6), 캐스트 식(§12.9.7), 단항 +
, -
, ~
, *
식, 이항 +
, -
, *
, /
, %
, <<
, >>
, <
, <=
, >
, >=
, ==
, !=
, is
, as
, &
, |
, ^
식(§12.10, §12.11, §12.12, §12.13), 복합 할당 식(§12.21.4), checked
및 unchecked
식(§12.8.20), 배열 및 대리자 생성 식(§12.8.17), 및 await
식(§12.9.8)에 적용되는 규칙은 다음과 같습니다.
이러한 각 식에는 조건부로 고정된 순서로 평가되는 하나 이상의 하위 식이 있습니다.
예: 이진
%
연산자는 연산자의 왼쪽, 오른쪽을 평가합니다. 인덱싱 작업은 인덱싱된 식을 평가한 다음 각 인덱스 식을 왼쪽에서 오른쪽으로 순서대로 평가합니다. 끝 예제
expr, expr, ..., expr, 하위 식이 있는 식 expr의 경우 다음 순서로 평가됩니다.
- expr의 시작 부분에 있는 v의 명확한 할당 상태는 expr 의 시작 부분에 있는 명확한 할당 상태와 동일합니다.
- expri의 시작 부분에 있는 v의 명확한 할당 상태(i 보다 큼)는 expri₋2의 끝에 있는 명확한 할당 상태와 동일합니다.
- expr의 끝에 있는 v의 명확한 할당 상태는 expr의 끝에 있는 명확한 할당 상태와 동일합니다.
9.4.4.24 호출 식 및 개체 만들기 식
호출할 메서드가 partial 메서드 선언을 구현하지 않는 부분 메서드이거나 호출을 생략하는 조건부 메서드(§22.5.3.2)인 경우 호출 후 v의 명확한 할당 상태는 호출 전 v의 명확한 할당 상태와 동일합니다. 그렇지 않으면 다음 규칙이 적용됩니다.
양식의 호출 식 expr 의 경우:
«primary_expression» ( «arg₁», «arg₂», … , «argₓ» )
또는 폼의 개체 만들기 식 expr 입니다.
new «type» ( «arg₁», «arg₂», … , «argₓ» )
- 호출 식의 경우 primary_expression 이전 v의 명확한 할당 상태는 expr 이전의 v 상태와 동일합니다.
- 호출 식의 경우 arg 이전 v의 명확한 할당 상태는 primary_expression 이후의 v 상태와 동일합니다.
- 개체 만들기 식의 경우 arg 이전 v의 명확한 할당 상태는 expr 이전의 v 상태와 동일합니다.
- 각 인수 argi에 대해 argi 이후의 v의 명확한 할당 상태는 정규 식 규칙에 의해 결정되며, 모든
in
또는out
ref
한정자를 무시합니다. - 1보다 큰 i에 대한 각 인수 argi에 대해 argi 이전 v의 명확한 할당 상태는 argi₋1 이후의 v 상태와 동일합니다.
- 변수 v가 인수(즉, "out
out
" 형식의 인수)로 인수로 전달되는 경우 expr 이후의 v 상태가 확실히 할당됩니다. 그렇지 않으면 expr 이후의 v 상태는 arg 이후 v의 상태와 동일합니다. - 배열 이니셜라이저(§12.8.17.5), 객체 이니셜라이저(§12.8.17.3), 컬렉션 이니셜라이저(§12.8.17.4) 및 익명 객체 이니셜라이저(§12.8.17.7)의 명확한 할당 여부는 이러한 구성 요소가 정의되는 확장에 의해 결정됩니다.
9.4.4.25 단순 할당 식
식 e의 할당 대상 집합을 다음과 같이 정의합니다.
- e가 튜플 식인 경우 e의 할당 대상은 e 요소의 할당 대상 통합입니다.
- 그렇지 않으면 e의 할당 대상은 e입니다.
폼의 expr 식의 경우:
«expr_lhs» = «expr_rhs»
- expr_lhs 이전 v의 명확한 할당 상태는 expr 이전의 v의 명확한 할당 상태와 동일합니다.
- expr_rhs 이전 v의 명확한 할당 상태는 expr_lhs 이후의 v의 명확한 할당 상태와 동일합니다.
- v가 expr_lhs 할당 대상인 경우 expr 이후 v의 명확한 할당 상태가 확실히 할당됩니다. 그렇지 않은 경우 할당이 구조체 형식의 인스턴스 생성자 내에서 발생하고 v가 생성되는 인스턴스에서 자동으로 구현된 속성 P의 숨겨진 지원 필드이고 P를 지정하는 속성 액세스가 expr_lhs 대상인 경우 expr 이후 v의 명확한 할당 상태가 확실히 할당됩니다. 그렇지 않으면 expr 이후 v의 명확한 할당 상태는 expr_rhs 후 v의 명확한 할당 상태와 동일합니다.
예제: 다음 코드에서
class A { static void F(int[] arr) { int x; arr[x = 1] = x; // ok } }
변수
x
는 두 번째 단순 할당의 왼쪽으로 평가된 후arr[x = 1]
확실히 할당된 것으로 간주됩니다.끝 예제
9.4.4.26 식
폼의 expr 식의 경우:
«expr_first» && «expr_second»
- expr_first 이전 v의 명확한 할당 상태는 expr 이전의 v의 명확한 할당 상태와 동일합니다.
- expr_second 이전 v의 명확한 할당 상태는 expr_first 이후v의 상태가 확실히 할당되거나 "true 식 후에 확실히 할당된" 경우에만 확실히 할당됩니다. 그렇지 않으면 확실히 할당되지 않습니다.
- expr 이후의 v의 명확한 할당 상태는 다음을 통해 결정됩니다.
- expr_first 후 v의 상태가 확실히 할당된 경우 expr 이후 v의 상태가 확실히 할당됩니다.
- 그렇지 않으면 expr_second 이후 v의 상태가 확실히 할당되고 expr_first 이후의 v 상태가 "false 식 후에 확실히 할당"되는 경우 expr 이후의 v 상태가 확실히 할당됩니다.
- 그렇지 않으면 expr_second 이후 v의 상태가 확실히 할당되거나 "true 식 후에 확실히 할당"되는 경우 expr 이후의 v 상태는 "true 식 후에 확실히 할당"됩니다.
- 그렇지 않은 경우 expr_first 이후 v의 상태가 "false 식 후에 확실히 할당됨"이고 expr_second 후 v의 상태가 "false 식 후에 확실히 할당됨"이면 expr 이후의 v 상태는 "false 식 후에 확실히 할당"됩니다.
- 그렇지 않으면 expr 이후의 v 상태가 확실히 할당되지 않습니다.
예제: 다음 코드에서
class A { static void F(int x, int y) { int i; if (x >= 0 && (i = y) >= 0) { // i definitely assigned } else { // i not definitely assigned } // i not definitely assigned } }
변수
i
는 문의 포함된 문if
중 하나에서 확실히 할당된 것으로 간주되지만 다른 문에는 할당되지 않습니다. 메서드if
의F
문에서는 식 실행이 항상 이 포함된 문의 실행i
보다 우선하기 때문에 첫 번째 포함된 문에 변수(i = y)
가 확실히 할당됩니다. 반면, 변수i
는 false를 테스트하여 변수가 할당되지 않았기 때문에x >= 0
두 번째 포함된 문에i
확실히 할당되지 않습니다.끝 예제
9.4.4.27 || 식
폼의 expr 식의 경우:
«expr_first» || «expr_second»
- expr_first 이전 v의 명확한 할당 상태는 expr 이전의 v의 명확한 할당 상태와 동일합니다.
- expr_second 이전 v의 명확한 할당 상태는 expr_first 이후v의 상태가 확실히 할당되거나 "true 식 후에 확실히 할당된" 경우에만 확실히 할당됩니다. 그렇지 않으면 확실히 할당되지 않습니다.
- expr 이후의 v에 대한 명확한 할당 문은 다음을 통해 결정됩니다.
- expr_first 후 v의 상태가 확실히 할당된 경우 expr 이후 v의 상태가 확실히 할당됩니다.
- 그렇지 않으면 expr_second 이후 v의 상태가 확실히 할당되고 expr_first 이후의 v 상태가 "true 식 후에 확실히 할당"되는 경우 expr 이후의 v 상태가 확실히 할당됩니다.
- 그렇지 않으면 expr_second 후 v의 상태가 확실히 할당되거나 "false 식 후에 확실히 할당"되는 경우 expr 이후의 v 상태는 "false 식 후에 확실히 할당"됩니다.
- 그렇지 않은 경우 expr_first 이후 v의 상태가 "true 식 후에 확실히 할당됨"이고 expr_ 초 이후의 v 상태가 "true 식 후에 확실히 할당됨"이면 expr 이후의 v 상태는 "true 식 후에 확실히 할당됨"입니다.
- 그렇지 않으면 expr 이후의 v 상태가 확실히 할당되지 않습니다.
예제: 다음 코드에서
class A { static void G(int x, int y) { int i; if (x >= 0 || (i = y) >= 0) { // i not definitely assigned } else { // i definitely assigned } // i not definitely assigned } }
변수
i
는 문의 포함된 문if
중 하나에서 확실히 할당된 것으로 간주되지만 다른 문에는 할당되지 않습니다. 메서드if
의G
문에서는 식i
실행이 항상 이 포함된 문의 실행보다 우선하기 때문에 변수(i = y)
가 두 번째 포함된 문에 확실히 할당됩니다. 반면, 변수i
는 true를 테스트하여 변수x >= 0
가 할당되지 않았기 때문에i
첫 번째 포함된 문에 확실히 할당되지 않습니다.끝 예제
9.4.4.28 ! 식
폼의 expr 식의 경우:
! «expr_operand»
- expr_operand 이전 v의 명확한 할당 상태는 expr 이전의 v의 명확한 할당 상태와 동일합니다.
- expr 이후의 v의 명확한 할당 상태는 다음을 통해 결정됩니다.
- expr_operand 이후의
v
상태가 확실히 할당된 경우 expr 이후의v
상태가 확실히 할당됩니다. - 그렇지 않으면 expr_operand 이후의
v
상태가 "false 식 후에 확실히 할당"된 경우 expr 이후의v
상태는 "true 식 후에 확실히 할당"됩니다. - 그렇지 않으면 expr_operand 이후의
v
상태가 "true 식 후에 확실히 할당됨"인 경우 expr 이후의 v 상태는 "false 식 후에 확실히 할당"됩니다. - 그렇지 않으면 expr
v
의 상태가 확실히 할당되지 않습니다.
- expr_operand 이후의
9.4.4.29 ?? 식
폼의 expr 식의 경우:
«expr_first» ?? «expr_second»
- expr_first 이전 v의 명확한 할당 상태는 expr 이전의 v의 명확한 할당 상태와 동일합니다.
- expr_second 이전의 v의 명확한 할당 상태는 expr_first 후 v의 명확한 할당 상태와 동일합니다.
- expr 이후의 v에 대한 명확한 할당 문은 다음을 통해 결정됩니다.
- expr_first
null
expr 이후 v의 상태는 expr_second 이후의 v 상태와 동일합니다. - 그렇지 않으면 expr 이후의 v 상태는 expr_first 후 v의 명확한 할당 상태와 동일합니다.
- expr_first
9.4.4.30 ?: 식
폼의 expr 식의 경우:
«expr_cond» ? «expr_true» : «expr_false»
- expr_cond 이전 v의 명확한 할당 상태는 expr 이전의 v 상태와 동일합니다.
- expr_true 이전 v의 명확한 할당 상태는 expr_cond 이후의 v 상태가 확실히 할당되거나 "true 식 후에 확실히 할당"되는 경우 확실히 할당됩니다.
- expr_false 이전 v의 명확한 할당 상태는 expr_cond 이후의 v 상태가 확실히 할당되거나 "false 식 후에 확실히 할당"되는 경우 확실히 할당됩니다.
- expr 이후의 v의 명확한 할당 상태는 다음을 통해 결정됩니다.
- expr_cond
true
expr 이후 v의 상태는 expr_true 이후의 v 상태와 동일합니다. - 그렇지 않으면 expr_cond 값이 있는 상수 식(§12.23)인 경우 expr 이후 v
false
상태는 expr_false 이후의 v 상태와 동일합니다. - 그렇지 않으면 expr_true 후 v의 상태가 확실히 할당되고 expr_false 후 v의 상태가 확실히 할당된 경우 expr 이후의 v 상태가 확실히 할당됩니다.
- 그렇지 않으면 expr 이후의 v 상태가 확실히 할당되지 않습니다.
- expr_cond
9.4.4.31 익명 함수
본문(블록 또는 식) 본문이 있는 lambda_expression 또는 anonymous_method_expressionexpr의 경우:
- 매개 변수의 명확한 할당 상태는 명명된 메서드의 매개 변수와 동일합니다(§9.2.6, §9.2.7, §9.2.8).
- 본문 앞의 외부 변수 v의 명확한 할당 상태는 expr 이전의 v 상태와 동일합니다. 즉, 외부 변수의 명확한 할당 상태는 익명 함수의 컨텍스트에서 상속됩니다.
- expr 이후의 외부 변수 v의 명확한 할당 상태는 expr 이전의 v 상태와 동일합니다.
예: 예제
class A { delegate bool Filter(int i); void F() { int max; // Error, max is not definitely assigned Filter f = (int n) => n < max; max = 5; DoWork(f); } void DoWork(Filter f) { ... } }
는 무명 함수가 선언된 위치에 max가 확실히 할당되지 않았기 때문에 컴파일 시간 오류를 생성합니다.
끝 예제
예: 예제
class A { delegate void D(); void F() { int n; D d = () => { n = 1; }; d(); // Error, n is not definitely assigned Console.WriteLine(n); } }
는 익명 함수의 할당이 익명 함수 외부의
n
명확한 할당n
상태에 영향을 주지 않으므로 컴파일 시간 오류를 생성합니다.끝 예제
9.4.4.32 Throw 식
폼의 expr 식의 경우:
throw
thrown_expr
- thrown_expr 이전 v의 명확한 할당 상태는 expr 이전의 v 상태와 동일합니다.
- expr 이후 v의 명확한 할당 상태는 "확실히 할당"됩니다.
9.4.4.33 로컬 함수의 변수에 대한 규칙
로컬 함수는 부모 메서드의 컨텍스트에서 분석됩니다. 로컬 함수에 중요한 제어 흐름 경로에는 함수 호출 및 대리자 변환이라는 두 가지 제어 흐름 경로가 있습니다.
각 로컬 함수의 본문에 대한 명확한 할당은 각 호출 사이트에 대해 별도로 정의됩니다. 호출할 때마다 로컬 함수에 의해 캡처된 변수는 호출 시점에 확실히 할당된 경우 확실히 할당된 것으로 간주됩니다. 제어 흐름 경로도 이 시점에서 로컬 함수 본문에 존재하며 연결할 수 있는 것으로 간주됩니다. 로컬 함수를 호출한 후 함수(return
문, 문, yield
await
식)를 벗어나는 모든 제어 지점에서 확실히 할당된 캡처된 변수는 호출 위치 후에 확실히 할당된 것으로 간주됩니다.
대리자 변환에는 로컬 함수 본문에 대한 제어 흐름 경로가 있습니다. 캡처된 변수는 변환 전에 확실히 할당된 경우 본문에 대해 확실히 할당됩니다. 로컬 함수에 의해 할당된 변수는 변환 후에 할당된 것으로 간주되지 않습니다.
참고: 위의 내용은 모든 로컬 함수 호출 또는 대리자 변환 시 본문이 명확한 할당을 위해 다시 분석됨을 의미합니다. 컴파일러는 각 호출 또는 대리자 변환 시 로컬 함수의 본문을 다시 분석할 필요가 없습니다. 구현은 해당 설명과 동일한 결과를 생성해야 합니다. 끝 메모
예제: 다음 예제에서는 로컬 함수에서 캡처된 변수에 대한 명확한 할당을 보여 줍니다. 로컬 함수가 캡처된 변수를 쓰기 전에 읽는 경우 로컬 함수를 호출하기 전에 캡처된 변수를 확실히 할당해야 합니다. 로컬 함수
F1
는 할당하지 않고 읽습니다s
. 이전에 호출F1
된 경우 확실히 할당된 경우s
오류입니다.F2
는 읽기 전에를 할당합니다 i
. 그것은 확실히 할당되기 전에i
호출 할 수 있습니다. 또한F3
에 확실히 할당F2
되어 있으므로s2
이후에F2
호출 할 수 있습니다.void M() { string s; int i; string s2; // Error: Use of unassigned local variable s: F1(); // OK, F2 assigns i before reading it. F2(); // OK, i is definitely assigned in the body of F2: s = i.ToString(); // OK. s is now definitely assigned. F1(); // OK, F3 reads s2, which is definitely assigned in F2. F3(); void F1() { Console.WriteLine(s); } void F2() { i = 5; // OK. i is definitely assigned. Console.WriteLine(i); s2 = i.ToString(); } void F3() { Console.WriteLine(s2); } }
끝 예제
9.4.4.34 is-pattern 식
폼의 expr 식의 경우:
expr_operand 패턴입니다.
- expr_operand 이전 v의 명확한 할당 상태는 expr 이전의 v의 명확한 할당 상태와 동일합니다.
- 변수 'v'가 패턴으로 선언된 경우 expr 이후의 'v'의 명확한 할당 상태는 "true일 때 확실히 할당"됩니다.
- 그렇지 않으면 expr 이후의 'v'의 명확한 할당 상태는 expr_operand 이후의 'v'의 명확한 할당 상태와 동일합니다.
9.5 변수 참조
variable_reference변수로 분류되는 식입니다. variable_reference 현재 값을 가져오고 새 값을 저장하기 위해 액세스할 수 있는 스토리지 위치를 나타냅니다.
variable_reference
: expression
;
참고: C 및 C++에서는 variable_reference lvalue라고합니다. 끝 메모
9.6 변수 참조의 원자성
다음 데이터 형식의 읽기 및 쓰기는 원자성bool
char
byte
sbyte
short
ushort
uint
int
float
이어야 합니다. 또한 이전 목록에서 기본 형식을 사용하는 열거형 형식의 읽기 및 쓰기도 원자성이어야 합니다. 사용자 정의 형식뿐만 아니라 , long
, ulong
및를 비롯한 double
다른 형식의 읽기 및 decimal
쓰기는 원자성일 필요가 없습니다. 이러한 용도로 설계된 라이브러리 함수 외에는 증가 또는 감소와 같은 원자성 읽기-수정-쓰기가 보장되지 않습니다.
9.7 참조 변수 및 반환
9.7.1 일반
참조 변수는 참조(§9.2.6)라는 다른 변수를 참조하는 변수입니다. 참조 변수는 한정자를 사용하여 ref
선언된 지역 변수입니다.
참조 변수는 참조 값이 아닌 참조에 variable_reference (§9.5)를 저장합니다. 참조 변수가 사용되는 경우 값이 필요한 경우 참조의 값이 반환됩니다. 마찬가지로 참조 변수가 할당 대상인 경우 할당된 참조 변수입니다. 참조 변수가 참조하는 변수(예: 참조에 대해 저장된 variable_reference )는 ref 할당(= ref
)을 사용하여 변경할 수 있습니다.
예제: 다음 예제에서는 참조가 배열의 요소인 로컬 참조 변수를 보여 줍니다.
public class C { public void M() { int[] arr = new int[10]; // element is a reference variable that refers to arr[5] ref int element = ref arr[5]; element += 5; // arr[5] has been incremented by 5 } }
끝 예제
참조 반환은 return-by-ref 메서드(§15.6.1)에서 반환된 variable_reference. 이 variable_reference 참조 반환의 참조입니다.
예제: 다음 예제에서는 참조가 배열 필드의 요소인 참조 반환을 보여 줍니다.
public class C { private int[] arr = new int[10]; public ref readonly int M() { // element is a reference variable that refers to arr[5] ref int element = ref arr[5]; return ref element; // return reference to arr[5]; } }
끝 예제
9.7.2 참조 안전 컨텍스트
9.7.2.1 일반
모든 참조 변수는 참조 변수의 ref-safe-context가 참조의 ref-safe-context보다 크지 않도록 하는 안전 규칙을 준수합니다.
참고: 안전 컨텍스트의 관련 개념은 연결된 제약 조건 과 함께 (§16.4.12)에 정의되어 있습니다. 끝 메모
변수의 경우 해당 변수의 ref-safe-context는 해당 변수에 대한 variable_reference(§9.5)가 유효한 컨텍스트입니다. 참조 변수의 참조에는 참조 변수 자체의 ref-safe-context만큼 넓은 ref-safe-context가 있어야 합니다.
참고: 컴파일러는 프로그램 텍스트의 정적 분석을 통해 ref-safe-context를 결정합니다. ref-safe-context는 런타임에 변수의 수명을 반영합니다. 끝 메모
다음 세 가지 ref-safe-contexts가 있습니다.
선언 블록: 지역 변수에 대한 변수_참조의 ref-safe-context(§9.2.9.1)는 그 지역 변수의 범위(§13.6.2)이며, 그 범위 안의 모든 중첩된 포함 문장을 포함합니다.
지역 변수에 대한 variable_reference 참조 변수가 해당 변수의 ref-safe-context 내에서 선언된 경우에만 참조 변수에 대한 유효한 참조입니다.
function-member: 함수 내에서 다음 중 어느 variable_reference 함수 멤버의 ref-safe-context가 있습니다.
- 클래스 멤버 함수의 암시적 함수를 포함하여 함수 멤버 선언의 값 매개 변수(
this
) 및 - 해당 필드와 함께 구조체 멤버 함수의 암시적 참조(
ref
) 매개 변수(§15.6.2.3.3)this
입니다.
참조 변수가 동일한 함수 멤버에 선언된 경우에만 함수 멤버의 ref-safe-context가 있는 variable_reference 유효한 참조입니다.
- 클래스 멤버 함수의 암시적 함수를 포함하여 함수 멤버 선언의 값 매개 변수(
caller-context: 함수 내에서 다음 중 어느 variable_reference 호출자 컨텍스트의 ref-safe-context가 있습니다.
호출자 컨텍스트의 ref-safe-context를 사용하는 variable_reference 참조 반환의 참조일 수 있습니다.
이러한 값은 가장 좁은(선언 블록)에서 가장 넓은(호출자 컨텍스트)로 중첩 관계를 형성합니다. 중첩된 각 블록은 다른 컨텍스트를 나타냅니다.
예제: 다음 코드는 다른 ref-safe-contexts의 예를 보여 줍니다. 선언은 참조에 대한 ref-safe-context를 변수의 초기화 식으로
ref
표시합니다. 이 예제에서는 참조 반환에 대한 ref-safe-context를 보여 줍니다.public class C { // ref safe context of arr is "caller-context". // ref safe context of arr[i] is "caller-context". private int[] arr = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; // ref safe context is "caller-context" public ref int M1(ref int r1) { return ref r1; // r1 is safe to ref return } // ref safe context is "function-member" public ref int M2(int v1) { return ref v1; // error: v1 isn't safe to ref return } public ref int M3() { int v2 = 5; return ref arr[v2]; // arr[v2] is safe to ref return } public void M4(int p) { int v3 = 6; // context of r2 is declaration-block, // ref safe context of p is function-member ref int r2 = ref p; // context of r3 is declaration-block, // ref safe context of v3 is declaration-block ref int r3 = ref v3; // context of r4 is declaration-block, // ref safe context of arr[v3] is caller-context ref int r4 = ref arr[v3]; } }
끝 예제입니다.
예: 형식의 경우
struct
암시적this
매개 변수가 참조 매개 변수로 전달됩니다. 함수 멤버인 형식 필드의struct
ref-safe-context는 참조 반환을 통해 해당 필드를 반환하지 못하게 합니다. 이 규칙은 다음 코드를 방지합니다.public struct S { private int n; // Disallowed: returning ref of a field. public ref int GetN() => ref n; } class Test { public ref int M() { S s = new S(); ref int numRef = ref s.GetN(); return ref numRef; // reference to local variable 'numRef' returned } }
끝 예제입니다.
9.7.2.2 로컬 변수 ref safe 컨텍스트
지역 변수 v
의 경우:
- 참조 변수인 경우
v
ref-safe-context는 초기화 식의 ref-safe-context와 동일합니다. - 그렇지 않으면 ref-safe-context가 선언 블록입니다.
9.7.2.3 매개 변수 참조 안전 컨텍스트
매개 변수 p
의 경우:
- 참조 또는 입력 매개 변수인 경우
p
ref-safe-context는 호출자 컨텍스트입니다. 입력 매개 변수인 경우p
쓰기 가능ref
으로 반환할 수 없지만 .로ref readonly
반환할 수 있습니다. - 출력 매개 변수인 경우
p
ref-safe-context는 호출자 컨텍스트입니다. - 그렇지 않은 경우 구조체 형식의 매개 변수인 경우
p
this
ref-safe-context는 함수 멤버입니다. - 그렇지 않으면 매개 변수는 값 매개 변수이고 ref-safe-context는 함수 멤버입니다.
9.7.2.4 필드 참조 안전 컨텍스트
필드에 대한 참조를 지정하는 변수의 경우: e.F
- 참조 형식인 경우
e
ref-safe-context는 호출자 컨텍스트입니다. - 그렇지 않으면 값 형식인 경우
e
ref-safe-context는 ref-safe-context와e
동일합니다.
9.7.2.5 연산자
조건부 연산자(§12.18) c ? ref e1 : ref e2
및 참조 할당 연산자 = ref e
(§12.21.1)는 참조 변수를 피연산자로 사용하고 참조 변수를 생성합니다. 이러한 연산자의 경우 결과의 ref-safe-context는 모든 ref
피연산자의 ref-safe-contexts 중에서 가장 좁은 컨텍스트입니다.
9.7.2.6 함수 호출
ref-returning 함수 호출로 인해 발생하는 변수 c
의 경우 ref-safe-context는 다음 컨텍스트 중 가장 좁습니다.
- 호출자 컨텍스트입니다.
- 모든
ref
및out
in
인수 식(수신기 제외)의 ref-safe-context입니다. - 각 입력 매개 변수에 대해 변수인 해당 식이 있고 변수 형식과 매개 변수 형식 간에 ID 변환이 있는 경우 변수의 ref-safe-context, 그렇지 않으면 가장 가까운 바깥쪽 컨텍스트입니다.
- 모든 인수 식(수신기 포함)의 안전 컨텍스트(§16.4.12)입니다.
예: 마지막 글머리 기호는 다음과 같은 코드를 처리하는 데 필요합니다.
ref int M2() { int v = 5; // Not valid. // ref safe context of "v" is block. // Therefore, ref safe context of the return value of M() is block. return ref M(ref v); } ref int M(ref int p) { return ref p; }
끝 예제
속성 호출 및 인덱서 호출(또는 get
set
)은 위의 규칙에 따라 기본 접근자의 함수 호출로 처리됩니다. 로컬 함수 호출은 함수 호출입니다.
9.7.2.7 값
값의 ref-safe-context는 가장 가까운 바깥쪽 컨텍스트입니다.
참고: 이 오류는 형식
M(ref d.Length)
의 위치d
와 같은dynamic
호출에서 발생합니다. 또한 입력 매개 변수에 해당하는 인수와도 일치합니다. 끝 메모
9.7.2.8 생성자 호출
생성자를 호출하는 식은 new
생성되는 형식을 반환하는 것으로 간주되는 메서드 호출(§9.7.2.6)과 동일한 규칙을 준수합니다.
9.7.2.9 참조 변수에 대한 제한 사항
- 참조 매개 변수나 출력 매개 변수, 입력 매개 변수,
ref
로컬 매개 변수 또는 형식의ref struct
매개 변수 또는 로컬은 람다 식 또는 로컬 함수에 의해 캡처되지 않습니다. - 참조 매개 변수나 출력 매개 변수, 입력 매개 변수 또는 형식의
ref struct
매개 변수는 반복기 메서드 또는async
메서드에 대한 인수가 아니어야 합니다. -
ref
로컬 또는 형식의ref struct
로컬은 문이나yield return
식의await
지점에서 컨텍스트에 있지 않습니다. - ref 재할당
e1 = ref e2
의 경우 ref-safe-context는e2
적어도 ref-safe-context와 같은 넓은 컨텍스트e1
여야 합니다. - ref return 문의
return ref e1
경우 ref-safe-context는e1
호출자 컨텍스트여야 합니다.
ECMA C# draft specification