12.1 일반
식은 연산자와 피연산자의 시퀀스입니다. 이 절은 구문, 피연산자 및 연산자의 계산 순서 및 식의 의미를 정의합니다.
12.2 표현 분류
12.2.1 일반
식의 결과는 다음 중 하나로 분류됩니다.
- 값입니다. 모든 값에는 연결된 형식이 있습니다.
- 변수입니다. 달리 지정하지 않는 한 변수는 명시적으로 형식화되고 연결된 형식, 즉 선언된 변수 형식을 가집니다. 암시적으로 형식화된 변수에는 연결된 형식이 없습니다.
- null 리터럴입니다. 이 분류에 속하는 식은 참조 형식 또는 nullable 값 형식으로 암시적으로 변환될 수 있습니다.
- 익명 함수입니다. 이 분류를 사용하는 식은 호환되는 대리자 형식 또는 식 트리 형식으로 암시적으로 변환될 수 있습니다.
- 튜플입니다. 모든 튜플에는 각각 식과 선택적 튜플 요소 이름이 있는 고정된 개수의 요소가 있습니다.
- 속성 액세스입니다. 모든 속성 액세스에는 연결된 형식, 즉 속성의 형식이 있습니다. 또한 속성 접근에 연결된 인스턴스 식이 있을 수 있습니다. 인스턴스 속성 액세스의 접근자가 호출되면 인스턴스 식을 계산한 결과가
this(§12.8.14)로 표시되는 인스턴스가 됩니다. - 인덱서 액세스입니다. 모든 인덱서 액세스에는 연결된 형식, 즉 인덱서의 요소 형식이 있습니다. 또한 인덱서 액세스에는 연결된 인스턴스 식과 연결된 인수 목록이 있습니다. 인덱서 액세스의 접근자가 호출되면 인스턴스 식 평가 결과가
this(§12.8.14)로 표현되는 인스턴스가 되고 인수 목록을 계산한 결과는 호출의 매개 변수 목록이 됩니다. - 아무것도. 메서드를 호출하여 반환 형식이
void일 때 이 식이 발생합니다. 아무것도로 분류되지 않은 식은 statement_expression 컨텍스트(§13.7) 또는 lambda_expression 본문(§12.21)에서만 유효합니다.
더 큰 식의 하위 식으로 발생하는 식의 경우 명시된 제한 사항을 사용하여 결과를 다음 중 하나로 분류할 수도 있습니다.
- 네임스페이스입니다. 이 분류를 가진 식은 member_access의 왼쪽에만 위치할 수 있습니다(§12.8.7). 다른 컨텍스트에서는 네임스페이스로 분류된 식이 컴파일 시 오류를 발생시킵니다.
- 형식 이 분류를 가진 식은 member_access의 왼쪽에만 위치할 수 있습니다(§12.8.7). 다른 상황에서 타입으로 분류된 식은 컴파일 오류를 발생시킵니다.
- 멤버 조회(§12.5)로 인해 오버로드된 메서드 집합인 메서드 그룹입니다. 메서드 그룹에는 연결된 인스턴스 식과 연결된 형식 인수 목록이 있을 수 있습니다. 인스턴스 메서드가 호출되면 인스턴스 식을 계산한 결과는
this(§12.8.14)로 표현되는 인스턴스가 됩니다. 메서드 그룹은 invocation_expression(§12.8.10) 또는 delegate_creation_expression(§12.8.17.5)에서 허용되며 호환되는 대리자 형식(§10.8)으로 암시적으로 변환할 수 있습니다. 다른 컨텍스트에서 메서드 그룹으로 분류된 식은 컴파일 시간 오류를 발생합니다. - 이벤트 액세스입니다. 모든 이벤트 액세스에는 연결된 형식, 즉 이벤트 유형이 있습니다. 또한 이벤트 액세스에는 연결된 인스턴스 식이 있을 수 있습니다. 이벤트 액세스는 및
+=연산자의-=왼쪽 피연산자로 나타날 수 있습니다(§12.23.5). 다른 모든 컨텍스트에서 이벤트 액세스로 분류된 표현은 컴파일 시간 오류를 유발합니다. 인스턴스 이벤트 액세서가 호출되면, 인스턴스 식을 계산한 결과는this으로 나타나는 인스턴스가 됩니다(§12.8.14). - 여러 컨텍스트에서 식에서 예외를 던지기 위해 사용할 수 있는 throw 식입니다. throw 식은 암시적 변환을 통해 모든 형식으로 변환될 수 있습니다.
속성 액세스나 인덱서 액세스는 get 접근자 또는 set 접근자를 호출함으로써 항상 값으로 다시 분류됩니다. 특정 접근자는 속성 또는 인덱서 액세스의 컨텍스트에 따라 결정됩니다. 액세스가 할당의 대상인 경우 set 접근자가 호출되어 새 값(§12.23.2)을 할당합니다. 그렇지 않으면 get 접근자가 호출되어 현재 값(§12.2.2)을 얻습니다.
인스턴스 접근자는 인스턴스의 속성, 이벤트 또는 인덱서에 대한 액세스입니다.
12.2.2 식의 값
식을 포함하는 대부분의 구문은 궁극적으로 식이 의 값으로를 나타내야 합니다. 이러한 경우 실제 식이 네임스페이스, 타입, 메서드 그룹 또는 아무것도 의미하지 않으면 컴파일 시간 오류가 발생합니다. 그러나 식이 속성 액세스, 인덱서 액세스 또는 변수를 나타내는 경우 속성, 인덱서 또는 변수의 값은 암시적으로 대체됩니다.
- 변수의 값은 변수로 식별되는 스토리지 위치에 현재 저장된 값일 뿐입니다. 변수 값이 얻어지기 전에 확실히 할당된 것으로 간주되지 않으면 컴파일 시간 오류가 발생합니다(§9.4).
- 속성 액세스 식의 값은 속성의 get 접근자를 호출하여 가져옵니다. 속성에 get 접근자가 없으면 컴파일 시간 오류가 발생합니다. 그렇지 않으면 함수 멤버 호출(§12.6.6)이 수행되고 호출 결과가 속성 액세스 식의 값이 됩니다.
- 인덱서 액세스 식의 값은 인덱서의 get 접근자를 호출하여 가져옵니다. 인덱서에 get 접근자가 없으면 컴파일 시간 오류가 발생합니다. 그렇지 않으면 함수 멤버 호출(§12.6.6)은 인덱서 액세스 식과 연결된 인수 목록과 함께 수행되고 호출 결과는 인덱서 액세스 식의 값이 됩니다.
- 튜플 식의 값은 암시적 튜플 변환(§10.2.13)을 튜플 식의 형식에 적용하여 가져옵니다. 형식이 없는 튜플 식의 값을 가져오는 것은 오류입니다.
12.3 정적 및 동적 바인딩
12.3.1 일반
바인딩 식의 형식 또는 값(인수, 피연산자, 수신기)에 따라 연산이 참조하는 항목을 결정하는 프로세스입니다. 예를 들어 메서드 호출의 바인딩은 수신자 및 인수의 형식에 따라 결정됩니다. 연산자의 바인딩은 피연산자의 형식에 따라 결정됩니다.
C#에서는 작업의 바인딩이 일반적으로 하위 식의 컴파일 시 타입에 따라 컴파일 시간에 결정됩니다. 마찬가지로 식에 오류가 포함된 경우 오류가 검색되어 컴파일 시간에 보고됩니다. 이 방법을 정적 바인딩이라고 합니다.
그러나 식이 동적 식(즉, 형식이 dynamic)인 경우 이는 참여하는 모든 바인딩이 컴파일 타임에 있는 형식이 아닌 런타임 형식을 기반으로 해야 임을 나타냅니다. 따라서 이러한 작업의 바인딩은 프로그램을 실행하는 동안 작업이 실행될 때까지 지연됩니다. 이를 동적 바인딩이라고 합니다.
작업이 동적으로 바인딩되면 컴파일 시간에 거의 또는 전혀 검사가 수행되지 않습니다. 대신 런타임 바인딩이 실패하면 런타임에 오류가 예외로 보고됩니다.
C#의 다음 작업에는 바인딩이 적용됩니다.
- 멤버 접근:
e.M - 메서드 호출:
e.M(e₁,...,eᵥ) - 대리자 호출:
e(e₁,...,eᵥ) - 요소 액세스:
e[e₁,...,eᵥ] - 개체 만들기: 새
C(e₁,...,eᵥ) - 오버로드된 단항 연산자:
+,-,!(논리 부정만 해당),~,++,--,true,false - 오버로드된 이진 연산자:
+,-,*,/,%,&,&&,|,||,??,^,<<,>>,==,!=,>,<,>=,<= - 대입 연산자:
=,= ref,+=,-=,*=,/=,%=,&=,|=,^=,<<=,>>=,??= - 암시적 및 명시적 변환
동적 식이 관련되지 않은 경우 C#은 기본적으로 정적 바인딩을 사용합니다. 즉, 선택 프로세스에서 하위 식의 컴파일 시간 형식이 사용됩니다. 그러나 위에 나열된 작업의 하위 식 중 하나가 동적 식인 경우 작업은 동적으로 바인딩됩니다.
메서드 호출이 동적으로 바인딩되고 수신기를 포함한 모든 매개 변수가 입력 매개 변수인 경우 컴파일 시간 오류입니다.
12.3.2 바인딩 시간
정적 바인딩은 컴파일 시간에 발생하는 반면 동적 바인딩은 런타임에 발생합니다. 다음 하위클래스의 바인딩 시간 용어는 바인딩이 발생하는 시기에 따라 컴파일 시간 또는 런타임을 나타냅니다.
예제: 다음은 정적 및 동적 바인딩 및 바인딩 시간의 개념을 보여 줍니다.
object o = 5; dynamic d = 5; Console.WriteLine(5); // static binding to Console.WriteLine(int) Console.WriteLine(o); // static binding to Console.WriteLine(object) Console.WriteLine(d); // dynamic binding to Console.WriteLine(int)처음 두 호출은 정적으로 바인딩됩니다. 인수의 컴파일 시간 형식에 따라
Console.WriteLine오버로드가 선택됩니다. 따라서 바인딩 시간은 컴파일 시간입니다.세 번째 호출은 동적으로 바인딩됩니다. 인수의 런타임 형식에 따라
Console.WriteLine오버로드가 선택됩니다. 인수가 동적 식이므로 컴파일 시간 형식이 동적이므로 발생합니다. 따라서 세 번째 호출의 바인딩 시간은 런타임입니다.예제 종료
12.3.3 동적 바인딩
이 하위 클래스는 유익합니다.
동적 바인딩을 사용하면 C# 프로그램이 동적 개체, 즉 C# 형식 시스템의 일반 규칙을 따르지 않는 개체와 상호 작용할 수 있습니다. 동적 개체는 다른 형식 시스템을 사용하는 다른 프로그래밍 언어의 개체이거나 다양한 작업에 대한 자체 바인딩 의미 체계를 구현하도록 프로그래밍 방식으로 설정된 개체일 수 있습니다.
동적 개체가 자체 의미 체계를 구현하는 메커니즘은 구현에서 정의됩니다. 지정된 인터페이스(다시 구현 정의)는 동적 개체에 의해 구현되어 C# 런타임에 특별한 의미 체계가 있음을 알릴 수 있습니다. 따라서 동적 개체에 대한 작업이 동적으로 바인딩될 때마다 이 사양에서 명시된 C#의 바인딩 의미 체계가 아닌 고유한 의미 체계가 인계됩니다.
동적 바인딩의 목적은 동적 개체와의 상호 운용을 허용하는 것이지만, C#에서는 동적 개체인지 여부에 관계없이 모든 개체에 대한 동적 바인딩을 허용합니다. 이렇게 하면 동적 개체에 대한 작업 결과가 항상 동적 개체는 아니지만 컴파일 타임에 프로그래머에 알려지지 않은 형식이므로 동적 개체를 보다 원활하게 통합할 수 있습니다. 또한 동적 바인딩은 관련된 개체가 동적 개체가 없는 경우에도 오류가 발생하기 쉬운 리플렉션 기반 코드를 제거하는 데 도움이 될 수 있습니다.
12.3.4 하위 식 유형
작업이 정적으로 바인딩된 경우 하위 식의 형식(예: 수신기 및 인수, 인덱스 또는 피연산자)은 항상 해당 식의 컴파일 시간 형식으로 간주됩니다.
작업이 동적으로 바인딩되는 경우 하위 식의 형식은 하위 식의 컴파일 시간 형식에 따라 다른 방식으로 결정됩니다.
- 컴파일 시간 형식 동적의 하위 식은 식이 런타임에 계산되는 실제 값의 형식을 갖는 것으로 간주됩니다.
- 컴파일 시간 형식이 형식 매개 변수인 하위 식은 런타임에 형식 매개 변수가 바인딩된 형식으로 간주됩니다.
- 그렇지 않으면 하위 식은 컴파일 시의 형식으로 간주됩니다.
12.4 연산자
12.4.1 일반
식은 피연산자와 연산자에서 생성됩니다. 식의 연산자는 피연산자에 적용할 연산을 나타냅니다.
예제: 연산자의 예로는
+,-,*,/및new있습니다. 피연산자의 예로는 리터럴, 필드, 지역 변수 및 식이 있습니다. 예제 종료
다음과 같은 세 가지 연산자가 있습니다.
- 단항 연산자 단항 연산자는 하나의 피연산자를 사용하고 접두사 표기법(예:
–x) 또는 접미사 표기법(예:x++)을 사용합니다. - 이진 연산자 이진 연산자는 두 개의 피연산자를 사용하고 모두 접두사 표기법(예:
x + y)을 사용합니다. - 3항 연산자입니다. 3항 연산자
?:하나만 존재합니다. 3개의 피연산자를 사용하고 접두사 표기법(c ? x : y)을 사용합니다.
식에서 연산자의 계산 순서는 연산자의 우선 순위 및 연산자의 결합성 따라 결정됩니다(§12.4.2).
식의 피연산자는 왼쪽에서 오른쪽으로 계산됩니다.
예제:
F(i) + G(i++) * H(i)에서, 메서드F가i의 이전 값을 사용하여 호출되고, 그 다음 메서드G가i의 이전 값으로 호출되며, 마지막으로 메서드H가 i의 새 값으로 호출됩니다. 이는 연산자 우선 순위와 별개이며 관련이 없습니다. 예제 종료
특정 연산자는 오버로드될 수 있습니다. 연산자 오버로드(§12.4.3)를 사용하면 피연산자 중 하나 또는 둘 다 사용자 정의 클래스 또는 구조체 형식인 작업에 대해 사용자 정의 연산자 구현을 지정할 수 있습니다.
12.4.2 연산자 우선 순위 및 결합성
식에 여러 연산자가 포함된 경우 연산자의 우선 순위 개별 연산자가 평가되는 순서를 제어합니다.
참조: 예를 들어,
x + y * z연산자가 이진x + (y * z)연산자보다 우선 순위가 높기 때문에 식*가+로 평가됩니다. 끝 메모
연산자의 우선 순위는 연결된 문법 프로덕션의 정의에 의해 설정됩니다.
참고: 예를 들어, additive_expression 은(는) 또는
+연산자로 구분된-의 시퀀스로 구성되어,+및-연산자가*,/,%연산자보다 우선 순위가 낮습니다. 끝 메모
참고: 다음 표에는 모든 연산자가 우선 순위가 높은 것부터 낮은 것까지 요약되어 있습니다.
조항 범주 연산자 §12.8 기본 x.yx?.yf(x)a[x]a?[x]x++x--x!newtypeofdefaultcheckeduncheckeddelegatestackalloc§12.9 단항 +-!x~^++x--x(T)xawait x§12.10 범위 ..§12.11 스위치 switch { … }§12.12 곱셈 */%§12.12 첨가물 +-§12.13 변화 <<>>§12.14 관계형 및 형식 테스트 <><=>=isas§12.14 평등 ==!=§12.15 논리적 AND &§12.15 논리 XOR ^§12.15 논리적 또는 \|§12.16 조건부 AND &&§12.16 조건부 OR \|\|§12.17 및 §12.18 널 병합 및 throw 표현식 ??throw x§12.20 조건부 ?:§12.23 및 §12.21 대입 및 람다 식 == ref*=/=%=+=-=<<=>>=&=^=\|==>??=끝 메모
우선 순위가 같은 두 연산자 간에 피연산자가 있을 때, 연산자의 결합성이 연산의 수행 순서를 결정합니다.
- 대입 연산자, 범위 연산자 및 null 병합 연산자를 제외하고 모든 이진 연산자는 왼쪽 연결 연산자이므로 작업이 왼쪽에서 오른쪽으로 수행됩니다.
예제:
x + y + z은(는)(x + y) + z으로 평가됩니다. 예제 종료 - 할당 연산자, null 병합 연산자 및 조건부 연산자(
?:)는 오른쪽 연관성()을 가지고 있습니다. 즉, 연산이 오른쪽에서 왼쪽으로 수행됩니다.예제:
x = y = z은(는)x = (y = z)으로 평가됩니다. 예제 종료 - 범위 연산자는 비연관적입니다. 즉, 범위 연산자의 왼쪽 피연산자나 오른쪽 피연산자가 range_expression 수 없습니다.
예: 둘 다
x..y..z비x..(y..z)연관적이므로..유효하지 않습니다. 예제 종료
우선 순위 및 결합성은 괄호를 사용하여 제어할 수 있습니다.
예제:
x + y * z은(는) 먼저y을(를)z로 곱한 다음 결과를x에 더하지만,(x + y) * z은(는) 먼저x과(와)y을(를) 더한 다음 결과를z로 곱합니다. 예제 종료
12.4.3 연산자 오버로드
모든 단항 및 이진 연산자는 미리 정의된 구현을 가지고 있습니다. 또한 클래스 및 구조체에 연산자 선언(§15.10)을 포함하여 사용자 정의 구현을 도입할 수 있습니다. 사용자 정의 연산자 구현은 항상 미리 정의된 연산자 구현보다 우선합니다. 적용 가능한 사용자 정의 연산자 구현이 없는 경우에만 §12.4.4 및 §12.4.5설명된 대로 미리 정의된 연산자 구현이 고려됩니다.
오버로드할 수 있는 단항 연산자는 다음과 같습니다.
+ - !(논리적 부정에만 해당)~ ++ -- true false
위에 나열된 연산자만 오버로드할 수 있습니다. 특히 null-forgiving 연산자(postfix !, §12.8.9) 또는 단항 인덱스 from-end 연산자(접두사 ^, (§12.9.6))를 오버로드할 수 없습니다.
참고:
truefalse식에는 명시적으로 사용되지 않지만(따라서 §12.4.2의 선행 테이블에 포함되지 않음) 부울 식(§12.26) 및 조건부(§12.20) 및 조건부 논리 연산자(§12.16)와 관련된 식 등 여러 식 컨텍스트에서 호출되기 때문에 연산자로 간주됩니다. 끝 메모
오버로드 가능한 이진 연산자는 다음과 같습니다.
+ - * / % & | ^ << >> == != > < <= >=
위에 나열된 연산자만 오버로드할 수 있습니다. 특히 멤버 액세스, 메서드 호출 또는 .., =&&||???:=>checkeduncheckednewtypeofdefaultas및 is 연산자를 오버로드할 수 없습니다.
이진 연산자가 오버로드되면 해당 복합 할당 연산자(있는 경우)도 암시적으로 오버로드됩니다.
예제: 연산자
*오버로드는 연산자*=오버로드이기도 합니다. 이 내용은 §12.23에 자세히 설명되어 있습니다. 예제 종료
(=) 할당 연산자 자체는 오버로드할 수 없습니다. 할당은 항상 값의 단순 저장소를 변수로 수행합니다(§12.23.2).
(T)x같은 캐스트 작업은 사용자 정의 변환(§10.5)을 제공하여 오버로드됩니다.
참고: 사용자 정의 변환은
is또는as연산자의 동작에 영향을 미치지 않습니다. 끝 메모
a[x]같은 요소 액세스는 오버로드 가능한 연산자로 간주되지 않습니다. 대신 사용자 정의 인덱싱은 인덱서(§15.9)를 통해 지원됩니다.
식에서 연산자는 연산자 표기법을 사용하여 참조되고 선언에서는 함수 표기법을 사용하여 연산자를 참조합니다. 다음 표에서는 단항 연산자와 이진 연산자에 대한 연산자와 기능 표기법 간의 관계를 보여 줍니다. 첫 번째 항목에서 «op»은 오버로드할 수 있는 단항 접두사 연산자를 표시합니다. 두 번째 항목에서 «op»은 단항 접두사 ++ 및 -- 연산자를 표시합니다. 세 번째 항목에서 «op»은 오버로드 가능한 이진 연산자를 표시합니다.
참고:
++및--연산자를 오버로드하는 예제는 §15.10.2참조하세요. 끝 메모
| 연산자 표기법 | 기능 표기법 |
|---|---|
«op» x |
operator «op»(x) |
x «op» |
operator «op»(x) |
x «op» y |
operator «op»(x, y) |
사용자 정의 연산자 선언에는 항상 매개 변수 중 하나 이상이 연산자 선언을 포함하는 클래스 또는 구조체 형식이어야 합니다.
참고: 따라서 사용자 정의 연산자가 미리 정의된 연산자와 동일한 서명을 가질 수 없습니다. 끝 메모
사용자 정의 연산자 선언은 연산자의 구문, 우선 순위 또는 결합성을 수정할 수 없습니다.
예제:
/연산자는 항상 이진 연산자이며, 항상 §12.4.2지정된 우선 순위 수준을 가지며 항상 왼쪽 연결입니다. 예제 종료
참고: 사용자 정의 연산자가 권장하는 계산을 수행할 수 있지만 직관적으로 예상되는 결과 이외의 결과를 생성하는 구현은 권장되지 않습니다. 예를 들어, 연산자
==의 구현은 두 피연산자가 같은지 비교하고 적절한bool결과를 반환해야 합니다. 끝 메모
§12.9에서 §12.23까지의 개별 연산자에 대한 설명은 연산자의 미리 정의된 구현과 각 연산자에 적용되는 추가 규칙을 지정합니다. 용어 단항 연산자 오버로드 확인, 이진 연산자 오버로드 확인, 숫자 승격, 그리고 리프팅된 연산자 정의는 다음의 하위 절에서 찾을 수 있습니다.
12.4.4 단항 연산자 오버로드 해결
형식이 «op» x 또는 x «op»인 연산은 다음과 같이 처리됩니다. 여기서 «op»은 오버로드 가능한 단항 연산자이고, x는 X형식의 표현식입니다.
- 작업
X대해operator «op»(x)제공하는 후보 사용자 정의 연산자 집합은 §12.4.6규칙을 사용하여 결정됩니다. - 후보 사용자 정의 연산자 집합이 비어 있지 않으면 이 집합이 작업에 관한 후보 연산자 집합이 됩니다. 그렇지 않을 경우, 승격된 형식을 포함하여 미리 정의된 이진
operator «op»구현이 해당 작업을 위한 후보 연산자의 집합이 됩니다. 지정된 연산자의 미리 정의된 구현은 연산자의 설명에 지정됩니다. 열거형 또는 대리자 형식에서 제공하는 미리 정의된 연산자는 두 피연산자의 바인딩 시점 형식이 열거형 또는 대리자 형식일 경우, 또는 만약 null 허용 형식인 경우 기본 형식이 열거형 또는 대리자 형식일 경우에만 이 집합에 포함됩니다. -
§12.6.4 오버로드 확인 규칙은 인수 목록
(x)관련하여 최상의 연산자를 선택하기 위해 후보 연산자 집합에 적용되며, 이 연산자는 오버로드 확인 프로세스의 결과가 됩니다. 오버로드 해석이 최상의 단일 연산자를 선택하지 못하면 바인딩-타임 오류가 발생합니다.
12.4.5 이진 연산자 오버로드 확인
형식 x «op» y연산입니다. 여기서 «op» 은 오버로드 가능한 이진 연산자이고, xX형식의 식이며, yY형식의 식이며 다음과 같이 처리됩니다.
- 작업
X에 대해Y및operator «op»(x, y)이 제공하는 후보 사용자 정의 연산자의 집합이 결정됩니다. 집합은X제공하는 후보 연산자와Y제공 한 후보 연산자의 조합으로 구성되며, 각각 §12.4.6규칙을 사용하여 결정됩니다. 결합된 집합의 경우 후보는 다음과 같이 병합됩니다.-
X및YID 변환이 가능하거나X및Y공통 기본 형식에서 파생된 경우 공유 후보 연산자는 결합된 집합에서 한 번만 발생합니다. -
X와Y간에 동일 변환이 있는 경우,«op»Y이 제공하는 연산자Y가«op»X가 제공하는X와 동일한 반환 형식을 가질 뿐만 아니라,«op»Y의 피연산자 형식이«op»X의 해당 피연산자 형식에 동일 변환을 가지면, 그 집합에서 오직«op»X만 발생합니다.
-
- 후보 사용자 정의 연산자 집합이 비어 있지 않으면 이 집합이 작업에 관한 후보 연산자 집합이 됩니다. 그렇지 않을 경우, 승격된 형식을 포함하여 미리 정의된 이진
operator «op»구현이 해당 작업을 위한 후보 연산자의 집합이 됩니다. 지정된 연산자의 미리 정의된 구현은 연산자의 설명에 지정됩니다. 미리 정의된 열거형 및 대리자 연산자의 경우 고려되는 유일한 연산자는 피연산자 중 하나의 바인딩 시간 형식인 열거형 또는 대리자 형식에서 제공하는 연산자뿐입니다. -
§12.6.4 오버로드 확인 규칙은 인수 목록
(x, y)관련하여 최상의 연산자를 선택하기 위해 후보 연산자 집합에 적용되며, 이 연산자는 오버로드 확인 프로세스의 결과가 됩니다. 오버로드 해석이 최상의 단일 연산자를 선택하지 못하면 바인딩-타임 오류가 발생합니다.
12.4.6 후보 사용자 정의 연산자
형식 T와 연산 operator «op»(A)에 대해, «op»가 오버로드 가능한 연산자이고 A가 인수 목록인 경우, 연산자 T에 대해 «op»(A)이 제공하는 후보 사용자 정의 연산자 집합은 다음과 같이 결정됩니다.
-
T₀유형을 판별합니다.Tnullable 값 형식인 경우T₀기본 형식입니다. 그렇지 않으면T₀T같습니다. -
operator «op»의 모든T₀선언 및 이러한 연산자의 모든 상승된 형식에 대하여, 인수 목록 에 대해 하나 이상의 연산자가 적용 가능하다면 (A), 후보 연산자 집합은T₀에 있는 적용 가능한 모든 연산자로 구성됩니다. - 그렇지 않으면,
T₀이object이면 후보 연산자 집합이 비어 있습니다. - 그렇지 않으면,
T₀이 제공하는 후보 연산자 집합은T₀의 직접 기본 클래스가 제공하는 후보 연산자 집합이거나,T₀이 형식 매개 변수인 경우T₀의 유효 기본 클래스가 제공하는 후보 연산자 집합입니다.
12.4.7 숫자 승격
12.4.7.1 일반
이 하위 클래스는 유익합니다.
§12.4.7 및 하위 항목은 다음의 결합된 효과를 요약한 것입니다.
- 암시적 숫자 변환 규칙(§10.2.3);
- 더 나은 변환을위한 규칙 (§12.6.4.7); 그리고
- 사용 가능한 산술(§12.12), 관계형(§12.14) 및 정수 논리 연산자(§12.15.2) 연산자입니다.
숫자 승격은 미리 정의된 단항 및 이진 숫자 연산자의 피연산자의 특정 암시적 변환을 자동으로 수행하는 것으로 구성됩니다. 숫자 승격은 고유한 메커니즘이 아니라 미리 정의된 연산자에게 오버로드 해상도를 적용하는 효과입니다. 숫자 승격은 특히 사용자 정의 연산자의 평가에 영향을 주지 않지만 사용자 정의 연산자는 유사한 효과를 나타내도록 구현할 수 있습니다.
숫자 승격의 예로 이진 * 연산자의 미리 정의된 구현을 고려합니다.
int operator *(int x, int y);
uint operator *(uint x, uint y);
long operator *(long x, long y);
ulong operator *(ulong x, ulong y);
float operator *(float x, float y);
double operator *(double x, double y);
decimal operator *(decimal x, decimal y);
이 연산자 집합에 오버로드 확인 규칙(§12.6.4)을 적용하면 피연산자 형식에서 암시적 변환이 존재하는 첫 번째 연산자를 선택하는 효과가 있습니다.
예제: 연산
b * s에서,b이byte이고s이short인 경우 오버로드 결정은operator *(int, int)을 최상의 연산자로 선택합니다. 따라서b및s이int로 변환되고, 결과의 형식은int입니다. 마찬가지로,i * d이(가)i이고int이(가)d인 작업double의 경우,overload해상도는operator *(double, double)을(를) 최상의 연산자로 선택합니다. 예제 종료
정보 텍스트의 끝입니다.
12.4.7.2 단항 숫자 승격
이 하위 클래스는 유익합니다.
단항 숫자 승격은 미리 정의된 단항 연산자 +, -및 ~의 피연산자에 대해 발생합니다. 단항 숫자 승격은 단순히 피연산자를 형식 sbyte, byte, short, ushort, 또는 char에서 형식 int로 변환하는 것으로 구성됩니다. 또한 단항 연산자 -의 경우, 단항 숫자 승격은 uint 형식의 피연산자를 long형식으로 변환합니다.
정보 텍스트의 끝입니다.
12.4.7.3 이진 숫자 승격
이 하위 클래스는 유익합니다.
이진 숫자 승급은 미리 정의된 +, -, *, /, %, &, |, ^, ==, !=, >, <, >=및 <= 이진 연산자의 피연산자에서 일어납니다. 이진 숫자 승격은 두 피연산자를 비관계형 연산자의 경우 연산의 결과 형식이 되는 공통 형식으로 암시적으로 변환합니다. 이진 숫자 승격은 여기에 표시되는 순서대로 다음 규칙을 적용하는 것으로 구성됩니다.
- 피연산자 중 하나가
decimal형식인 경우 다른 피연산자는decimal형식으로 변환되거나 다른 피연산자의 형식이float또는double경우 바인딩 시간 오류가 발생합니다. - 그렇지 않으면 피연산자 중 하나가
double형식이면 다른 피연산자는double형식으로 변환됩니다. - 그렇지 않으면 피연산자 중 하나가
float형식이면 다른 피연산자는float형식으로 변환됩니다. - 그렇지 않은 경우, 어느 하나의 피연산자가
ulong형식인 경우, 다른 피연산자는ulong형식으로 변환됩니다. 그렇지 않고 다른 피연산자가type sbyte,short,int또는long형식인 경우에는 바인딩 시간 오류가 발생합니다. - 그렇지 않으면 피연산자 중 하나가
long형식이면 다른 피연산자는long형식으로 변환됩니다. - 그렇지 않으면 피연산자 중 하나가
uint형식이고 다른 피연산자 형식이sbyte,short또는int경우 두 피연산자는 모두 형식long변환됩니다. - 그렇지 않으면 피연산자 중 하나가
uint형식이면 다른 피연산자는uint형식으로 변환됩니다. - 그렇지 않으면 두 피연산자는 모두
int형식으로 변환됩니다.
참고: 첫 번째 규칙은
decimal형식을double및float형식과 혼합하는 모든 작업을 허용하지 않습니다. 규칙은decimal형식과double및float형식 간에 암시적 변환이 없다는 사실에서 따릅니다. 끝 메모
주의: 또한 다른 피연산자가 부호 있는 정수 계열 형식일 때, 피연산자가
ulong형식이 될 수 없습니다. 그 이유는ulong전체 범위와 부호 있는 정수형을 나타낼 수 있는 정수형이 없기 때문입니다. 끝 메모
위의 두 경우 모두 캐스트 식을 사용하여 한 피연산자를 다른 피연산자와 호환되는 형식으로 명시적으로 변환할 수 있습니다.
예제: 다음 코드에서
decimal AddPercent(decimal x, double percent) => x * (1.0 + percent / 100.0);바인딩 시간 오류는
decimal와double를 곱할 수 없기 때문에 발생합니다. 오류는 다음과 같이 두 번째 피연산자를decimal명시적으로 변환하여 해결됩니다.decimal AddPercent(decimal x, double percent) => x * (decimal)(1.0 + percent / 100.0);예제 종료
정보 텍스트의 끝입니다.
12.4.8 승격된 연산자
해제된 연산자는 nullable이 아닌 값 형식에서 작동하는 미리 정의된 사용자 정의 연산자도 해당 형식의 nullable 형식과 함께 사용할 수 있도록 허용합니다. 리프트된 연산자는 다음에 설명된 대로 특정 요구 사항을 충족하는 미리 정의된 사용자 정의 연산자에서 생성됩니다.
- 단항 연산
+++---!자의 경우 피연산자 및 결과 형식이 모두 null을 허용하지 않는 값 형식인 경우 해제된 연산자 형식, , (논리 부정)^및~해제된 형식이 존재합니다. 승격된 형식은 피연산자 및 결과 형식에 단일?수정자를 추가하여 구성됩니다. 피연산자가null일 경우, 리프트된 연산자는null값을 생성합니다. 그렇지 않으면 상승된 연산자가 피연산자의 래핑을 풀고, 기본 연산자를 적용한 후, 결과를 래핑합니다. - 피연산자 및 결과 형식이 모두 nullable이 아닌 값 형식인 경우 이진 연산
+-*/%&|^..<<자, 및>>, 및 연산자의 해제된 형식이 존재합니다. 승격된 형식은 각 피연산자 및 결과 유형에 단일?수정자를 추가하여 구성됩니다. 리프팅된 연산자는 하나 또는 두 피연산자가null모두 있는 경우 값을 생성null합니다(&에 설명된 대로 형식의|연산자 및bool?연산자인 예외). 그렇지 않은 경우, 리프팅된 연산자는 피연산자를 해제하고, 기본적인 연산자를 적용하고, 결과를 래핑합니다. - 같음 연산자
==및!=경우 피연산자 형식이 모두 null을 허용하지 않는 값 형식이고 결과 형식이bool경우 연산자의 해제된 형식이 존재합니다. 해제된 폼은 각 피연산자 형식에 단일?한정자를 추가하여 생성됩니다. 리프트된 연산자는 두 개의null값이 같고null값이null값과 같지 않은 것으로 간주합니다. 두 피연산자가 모두 비null경우 해제된 연산자는 피연산자를 래핑 해제하고 기본 연산자를 적용하여bool결과를 생성합니다. - 관계형 연산자
<,>,<=, 및>=에 대해 피연산자 형식이 모두 null을 허용하지 않는 값 형식이고 결과 형식이bool일 경우, 연산자의 승격된 형식이 존재합니다. 해제된 폼은 각 피연산자 형식에 단일?한정자를 추가하여 생성됩니다. 리프트 연산자는 하나 또는 두 개의 피연산자가false인 경우null값을 생성한다. 그렇지 않으면 해제된 연산자는 피연산자를 래핑 해제하고 기본 연산자를 적용하여bool결과를 생성합니다.
12.5 멤버 조회
12.5.1 일반
멤버 조회는 형식 컨텍스트에서 이름의 의미가 결정되는 프로세스입니다. 멤버 조회는 식에서 simple_name(§12.8.4) 또는 member_access(§12.8.7)를 평가하는 과정의 일부로 발생할 수 있습니다.
멤버가 메서드 또는 이벤트이거나 대리자 형식(§21) 또는 형식 dynamic (§8.2.4)의 상수, 필드 또는 속성인 경우 멤버는 호출할 수 없는 것으로 간주됩니다.
멤버 조회는 멤버의 이름뿐만 아니라 멤버가 갖는 형식 매개 변수 수 및 멤버에 액세스할 수 있는지 여부도 고려합니다. 멤버 조회를 위해 제네릭 메서드와 중첩된 제네릭 형식은 해당 선언에 표시된 형식 매개 변수 수를 가지며 다른 모든 멤버에는 0개의 형식 매개 변수가 있습니다.
형식 N 내에서 이름 K에 대해 T 형식 인수를 사용하는 멤버 조회는 다음과 같이 처리됩니다.
- 첫째,
N이라는 액세스 가능한 멤버 집합이 결정됩니다.-
T형식 매개 변수인 경우, 집합은N의 기본 제약 조건 또는 보조 제약 조건(§15.2.5)으로 지정된 각 형식에서T이라는 이름으로 액세스 가능한 멤버의 집합과N에서object라는 이름으로 액세스 가능한 멤버의 집합의 합집합입니다. - 그렇지 않으면 집합은 상속된 멤버 및 의
N라고 명명된 모든 액세스 가능한(T) 멤버와,N의object라고 이름이 지정된 액세스 가능한 멤버로 구성됩니다.T생성된 형식인 경우 §15.3.3설명한 대로 형식 인수를 대체하여 멤버 집합을 가져옵니다.override한정자를 포함하는 멤버는 집합에서 제외됩니다.
-
- 다음으로,
K0이면 선언에 형식 매개 변수가 포함된 모든 중첩 형식이 제거됩니다.K0이 아니면 형식 매개 변수 수가 다른 모든 멤버가 제거됩니다.K0이면 형식 유추 프로세스(§12.6.3)가 형식 인수를 유추할 수 있으므로 형식 매개 변수가 있는 메서드는 제거되지 않습니다. - 그런 다음 멤버가 호출되면 호출할 수 없는 모든 멤버가 집합에서 제거됩니다.
- 다음으로, 다른 멤버에 의해 숨겨진 멤버가 집합에서 제거됩니다.
S.M의 모든 멤버에 대해,S가 선언된 타입인M의 집합에 다음 규칙이 적용됩니다.-
M상수, 필드, 속성, 이벤트 또는 열거형 멤버인 경우 기본 형식의S선언된 모든 멤버가 집합에서 제거됩니다. -
M형식 선언인 경우 기본 형식의S선언된 모든 비 형식이 집합에서 제거되고 기본 형식의M선언된S형식 매개 변수와 동일한 개수의 모든 형식 선언이 집합에서 제거됩니다. -
M가 메서드라면, 기본 형식S에 선언된 모든 비 메서드 멤버가 집합에서 제거됩니다.
-
- 다음으로 클래스 멤버에 의해 숨겨진 인터페이스 멤버가 집합에서 제거됩니다. 이 단계는
T형식 매개 변수이고Tobject이외의 유효 기본 클래스와 비어있지 않은 유효 인터페이스 집합(§15.2.5)이 있는 경우에만 적용됩니다.S.M의 유형으로 선언된S멤버를 포함하는 집합의 모든 멤버M에 대해,S이object이외의 클래스 선언인 경우 다음 규칙이 적용됩니다.-
M상수, 필드, 속성, 이벤트, 열거형 멤버 또는 형식 선언인 경우 인터페이스 선언에 선언된 모든 멤버가 집합에서 제거됩니다. -
M메서드인 경우 인터페이스 선언에 선언된 모든 비 메서드 멤버가 집합에서 제거되고 인터페이스 선언에 선언된M동일한 시그니처를 가진 모든 메서드가 집합에서 제거됩니다.
-
- 마지막으로 숨겨진 멤버를 제거하면 조회 결과가 결정됩니다.
- 집합이 메서드가 아닌 단일 멤버로 구성된 경우 이 멤버는 조회의 결과입니다.
- 그렇지 않으면 집합에 메서드만 포함된 경우 이 메서드 그룹은 조회의 결과입니다.
- 그렇지 않으면 조회가 모호하고 바인딩 시간 오류가 발생합니다.
형식 매개 변수와 인터페이스 외의 다른 형식에서의 멤버 조회, 그리고 상속 체인의 각 인터페이스가 정확히 0개 또는 1개의 직접 기본 인터페이스를 가지는 엄격한 단일 상속 인터페이스에서의 멤버 조회에 대해, 조회 규칙의 효과는 파생 멤버가 동일한 이름이나 서명을 가진 기본 멤버를 숨긴다는 점입니다. 이러한 단일 상속 조회는 결코 모호하지 않습니다. 다중 상속 인터페이스의 멤버 조회에서 발생할 수 있는 모호성은 §19.4.11에 설명되어 있습니다.
참고: 이 단계는 한 종류의 모호성만 고려합니다. 멤버 조회로 인해 메서드 그룹이 생성되면 예를 들어 §12.6.4.1 및 §12.6.6.2설명한 대로 모호성으로 인해 메서드 그룹의 추가 사용이 실패할 수 있습니다. 끝 메모
12.5.2 기본 형식
멤버 조회를 위해 T 형식은 다음과 같은 기본 형식으로 간주됩니다.
-
T가object이거나dynamic일 경우에T는 기본 형식이 없습니다. -
T이 enum_type인 경우,T의 기본 형식은System.Enum,System.ValueType, 및object클래스 형식입니다. - 만약
T이 struct_type라면,T의 기본 형식은System.ValueType및object클래스 형식입니다.참고: nullable_value_type는 struct_type(§8.3.1)입니다. 끝 메모
- 만약
T가 class_type인 경우,T의 기본 형식은T의 기본 클래스들이며, 여기에는object클래스 유형도 포함됩니다. -
T이 인터페이스 유형인 경우,T의 기본 형식은T의 기본 인터페이스들이며, 클래스 유형은object입니다. -
T이 array_type인 경우,T의 기본 형식은System.Array및object클래스 형식입니다. -
T이(가) delegate_type인 경우,T의 기본 형식은System.Delegate및object클래스 형식입니다.
12.6 함수 멤버
12.6.1 일반
함수 멤버는 실행 문을 포함하는 멤버입니다. 함수 멤버는 항상 형식의 멤버이며 네임스페이스의 멤버일 수 없습니다. C#은 함수 멤버의 다음 범주를 정의합니다.
- 방법
- 속성
- 이벤트
- 인덱서
- 사용자 정의 연산자
- 인스턴스 생성자
- 정적 생성자
- 파이널라이저
종료자 및 정적 생성자(명시적으로 호출할 수 없음)를 제외하고 함수 멤버에 포함된 문은 함수 멤버 호출을 통해 실행됩니다. 함수 멤버 호출을 작성하기 위한 실제 구문은 특정 함수 멤버 범주에 따라 달라집니다.
함수 멤버 호출의 인수 목록(§12.6.2)은 함수 멤버의 매개 변수에 대한 실제 값 또는 변수 참조를 제공합니다.
제네릭 메서드를 호출하면 형식 유추를 사용하여 메서드에 전달할 형식 인수 집합을 결정할 수 있습니다. 이 프로세스는 §12.6.3설명되어 있습니다.
메서드, 인덱서, 연산자 및 인스턴스 생성자의 호출은 오버로드 확인을 사용하여 호출할 함수 멤버의 후보 집합을 결정합니다. 이 프로세스는 §12.6.4설명되어 있습니다.
바인딩 시 특정 함수 멤버가 오버로드 해석을 통해 확인되면, 함수 멤버를 호출하는 실제 런타임 프로세스는 §12.6.6에 설명됩니다.
참고: 다음 표에는 명시적으로 호출할 수 있는 함수 멤버의 6개 범주와 관련된 구문에서 발생하는 처리가 요약되어 있습니다. 테이블에서
e,x,y및value변수 또는 값으로 분류된 식을 나타내고,T형식으로 분류된 식을 나타내고,F메서드의 단순 이름이며,P속성의 단순 이름입니다.
건설하다 예시 설명 메서드 호출 F(x, y)오버로드 확인은 포함하는 클래스 또는 구조체에서 F최상의 방법을 선택하기 위해 적용됩니다. 메서드는 인수 목록(x, y)함께 호출됩니다. 만약 메서드가static아니라면, 인스턴스 표현은this입니다.T.F(x, y)오버로드 해결은 클래스 또는 구조체 F에서 가장 적합한 메서드T을 선택하기 위해 적용됩니다. 메서드가static않으면 바인딩 시간 오류가 발생합니다. 메서드는 인수 목록(x, y)함께 호출됩니다.e.F(x, y)오버로드 해결은 F형식이 제공하는 클래스, 구조체, 또는 인터페이스에서e의 가장 적합한 메서드를 선택하기 위해 적용됩니다. 메서드가static경우 바인딩 시간 오류가 발생합니다. 메서드는 인스턴스 식e과 인수 목록(x, y)을 사용하여 호출됩니다.속성 접근 P포함하는 클래스 또는 구조체에서 P속성의 get 접근자가 호출됩니다.P쓰기 전용인 경우 컴파일 시간 오류가 발생합니다.P이static이 아니라면, 인스턴스 표현은this입니다.P = value포함하는 클래스 또는 구조체에서 P속성의 set 접근자가 인수 목록(value)로 호출됩니다.P읽기 전용인 경우 컴파일 시간 오류가 발생합니다.P이static이 아니라면, 인스턴스 표현은this입니다.T.P클래스 또는 구조체 P의 속성T의 get 접근자가 호출됩니다.P이static이 아니거나P가 쓰기 전용인 경우 컴파일 시간 오류가 발생합니다.T.P = value클래스 또는 구조체 P의T속성의 set 접근자가 인수 목록(value)와 함께 호출됩니다.P가static이 아니거나P가 읽기 전용인 경우에 컴파일 시간 오류가 발생합니다.e.PP형식에서 제공하는 클래스, 구조체, 또는 인터페이스에서E속성의 get 접근자가 인스턴스 식e와 함께 호출됩니다. 바인딩 시간 오류는P이static인 경우 또는P가 쓰기 전용인 경우에 발생합니다.e.P = value클래스, 구조체 또는 인터페이스에 있는 P속성의 set 접근자가E유형으로 정의되며, 인스턴스 식e및 인수 목록(value)과 함께 호출됩니다. 바인딩 시간 오류는P가static인 경우나P가 읽기 전용인 경우에 발생합니다.이벤트 액세스 E += value포함하는 클래스 또는 구조체의 이벤트 E의 add 접근자가 실행됩니다.E이static이 아니라면, 인스턴스 표현은this입니다.E -= value포함하는 클래스 또는 구조체에서 이벤트 E의 제거 이벤트 접근자가 호출됩니다.E이static이 아니라면, 인스턴스 표현은this입니다.T.E += value클래스 또는 구조체 E의 이벤트T의 추가 접근자가 호출됩니다.E이static가 아니면 바인딩 시간 오류가 발생합니다.T.E -= value클래스 또는 구조체 E안의 이벤트T의 삭제 접근자가 호출됩니다.E이static가 아니면 바인딩 시간 오류가 발생합니다.e.E += valueE형식의 클래스, 구조체 또는 인터페이스에서 이벤트E의 추가 접근자는 인스턴스 식e로 호출됩니다.E이static인 경우, 바인딩 시간 오류가 발생합니다.e.E -= valueE형식에서 제공하는 클래스, 구조체 또는 인터페이스의 이벤트E의 제거 접근자가 인스턴스 식e과 함께 호출됩니다.E이static인 경우, 바인딩 시간 오류가 발생합니다.인덱서 액세스 e[x, y]오버로드 해소는 e형식에서 제공하는 클래스, 구조체 또는 인터페이스에서 가장 적절한 인덱서를 선택하기 위해 적용됩니다. 인덱서의 get 접근자는 인스턴스 식e및 인수 목록(x, y)과 함께 호출됩니다. 인덱서가 쓰기 전용인 경우 바인딩 시간 오류가 발생합니다.e[x, y] = value오버로드 해소는 e형식에서 제공하는 클래스, 구조체 또는 인터페이스에서 가장 적절한 인덱서를 선택하기 위해 적용됩니다. 인덱서의 set 접근자는 인스턴스 식e과 인수 목록(x, y, value)을 사용하여 호출됩니다. 인덱서가 읽기 전용인 경우 바인딩 시간 오류가 발생합니다.연산자 호출 -x오버로드 확인은 클래스에서 가장 적합한 단항 연산자를 선택하거나 x형식으로 지정된 구조체를 선택하기 위해 적용됩니다. 선택한 연산자는 인수 목록(x)함께 호출됩니다.x + y오버로드 해결은 x및y자료형에 따른 클래스 또는 구조체에서 최상의 이진 연산자를 선택하기 위해 적용됩니다. 선택한 연산자는 인수 목록(x, y)함께 호출됩니다.인스턴스 생성자 호출 new T(x, y)클래스 또는 구조체 T에서 최상의 인스턴스 생성자를 선택하기 위해 오버로드 해제가 적용됩니다. 인스턴스 생성자는 인수 목록(x, y)함께 호출됩니다.끝 메모
12.6.2 인수 목록
12.6.2.1 일반
모든 함수 멤버 및 대리자 호출에는 함수 멤버의 매개 변수에 대한 실제 값 또는 변수 참조를 제공하는 인수 목록이 포함됩니다. 함수 멤버 호출의 인수 목록을 지정하는 구문은 함수 멤버 범주에 따라 달라집니다.
- 인스턴스 생성자, 메서드, 인덱서 및 대리자의 경우 인수는 아래에 설명된 대로 argument_list지정됩니다. 인덱서의 경우 set 접근자를 호출할 때 인수 목록에는 할당 연산자의 오른쪽 피연산자로 지정된 식이 추가로 포함됩니다.
참고: 이 추가 인수는 오버로드 확인에 사용되지 않으며, set 접근자를 호출할 때만 사용됩니다. 끝 메모
- 속성의 경우 get 접근자를 호출할 때 인수 목록이 비어 있으며 set 접근자를 호출할 때 할당 연산자의 오른쪽 피연산자로 지정된 식으로 구성됩니다.
- 이벤트의 경우 인수 목록은
+=또는-=연산자의 오른쪽 피연산자로 지정된 식으로 구성됩니다. - 사용자 정의 연산자의 경우 인수 목록은 단항 연산자의 단일 피연산자 또는 이진 연산자의 두 피연산자로 구성됩니다.
속성(§15.7) 및 이벤트(§15.8)의 인수는 항상 값 매개 변수(§15.6.2.2)로 전달됩니다. 사용자 정의 연산자(§15.10)의 인수는 항상 값 매개 변수(§15.6.2.2) 또는 입력 매개 변수(§9.2.8)로 전달됩니다. 인덱서(§15.9)의 인수는 항상 값 매개 변수(§15.6.2.2), 입력 매개 변수(§9.2.8) 또는 매개 변수 배열(§15.6.2.4)으로 전달됩니다. 출력 및 참조 매개 변수는 이러한 함수 멤버 범주에 대해 지원되지 않습니다.
인스턴스 생성자, 메서드, 인덱서 또는 대리자 호출의 인수는 argument_list로 지정됩니다.
argument_list
: argument (',' argument)*
;
argument
: argument_name? argument_value
;
argument_name
: identifier ':'
;
argument_value
: expression
| 'in' variable_reference
| 'ref' variable_reference
| 'out' variable_reference
;
argument_list는 쉼표로 구분되는 하나 이상의 인수로 구성됩니다. 각 인수는 선택적 argument_name과 그 뒤에 오는 argument_value으로 구성됩니다.
argument_value 다음 형식 중 하나를 사용할 수 있습니다.
- 인수가 값 매개 변수로 전달되거나 입력 매개 변수로 변환된 다음 그렇게 전달됨을 나타내는
식 , 이는 (§12.6.4.2및 §12.6.2.3 .에서 설명된 대로) 결정됩니다 - 키워드
in뒤에 variable_reference(§9.5)가 옵니다. 인수가 입력 매개 변수로 전달됨을 나타냅니다(§15.6.2.3.2). 변수는 입력 매개 변수로 전달되기 전에 확실히 할당되어야 합니다(§9.4). - 키워드
ref뒤에 variable_reference(§9.5)가 옵니다. 인수가 참조 매개 변수로 전달됨을 나타냅니다(§15.6.2.3.3). 변수는 참조 매개 변수로 전달되기 전에 확실히 할당되어야 합니다(§9.4). - 키워드
out뒤에 variable_reference(§9.5)가 옵니다. 인수가 출력 매개 변수(§15.6.2.3.4)로 전달됨을 나타냅니다. 변수가 출력 매개 변수로 전달되는 함수 멤버 호출에 따라 변수가 확실히 할당된 것으로 간주됩니다(§9.4).
폼은 각각 인수의 매개변수 전달 모드를 결정합니다: 값 , 입력 , 참조 또는 출력 . 그러나 위에서 설명한 것처럼 값 전달 모드가 있는 인수는 입력 전달 모드가 있는 인수로 변환될 수 있습니다.
휘발성 필드(§15.5.4)를 입력, 출력 또는 참조 매개 변수로 전달하면 호출된 메서드에서 필드를 휘발성으로 처리할 수 없으므로 경고가 발생합니다.
12.6.2.2 해당 매개 변수
인수 목록의 각 인수에 대해 호출되는 함수 멤버 또는 대리자의 해당 매개 변수가 있어야 합니다.
다음에 사용되는 매개 변수 목록은 다음과 같이 결정됩니다.
- 클래스에 정의된 가상 메서드 및 인덱서의 경우 매개 변수 목록은 수신기의 정적 형식으로 시작하고 기본 클래스를 검색할 때 찾은 함수 멤버의 첫 번째 선언 또는 재정의에서 선택됩니다.
- 부분 메서드의 경우, 정의되는 부분 메서드 선언의 매개변수 목록이 사용됩니다.
- 다른 모든 함수 멤버 및 대리자의 경우 사용되는 매개 변수 목록이 하나만 있습니다.
인수 또는 매개 변수의 위치는 인수 목록 또는 매개 변수 목록에서 인수 또는 매개 변수 앞에 오는 매개 변수의 수로 정의됩니다.
함수 멤버 인수에 대한 해당 매개 변수는 다음과 같이 설정됩니다.
- 인스턴스 생성자, 메서드, 인덱서 및 대리자의 argument_list 인수:
- 매개 변수가 매개 변수 배열이고 함수 멤버가 확장된 형식으로 호출되지 않는 한 매개 변수 목록의 동일한 위치에서 매개 변수가 발생하는 위치 인수는 해당 매개 변수에 해당합니다.
- 매개 변수 목록에서 매개 변수 배열의 위치 또는 이후에 발생하는 확장된 형식으로 호출된 매개 변수 배열이 있는 함수 멤버의 위치 인수는 매개 변수 배열의 요소에 해당합니다.
- 명명된 인수는 매개 변수 목록에 있는 동일한 이름의 매개 변수에 해당합니다.
- 인덱서의 경우 set 접근자를 호출할 때 할당 연산자의 오른쪽 피연산자로 지정된 식은 set 접근자 선언의 암시적
value매개 변수에 해당합니다.
- 속성의 경우 get 접근자를 호출할 때 인수가 없습니다. set 접근자를 호출할 때 할당 연산자의 오른쪽 피연산자로 지정된 식은 set 접근자 선언의 암시적 값 매개 변수에 해당합니다.
- 사용자 정의 단항 연산자(변환 포함)의 경우 단일 피연산자는 연산자 선언의 단일 매개 변수에 해당합니다.
- 사용자 정의 이진 연산자의 경우 왼쪽 피연산자는 첫 번째 매개 변수에 해당하고 오른쪽 피연산자는 연산자 선언의 두 번째 매개 변수에 해당합니다.
- 명명되지 않은 인수는 명명된 외부 인수 또는 매개 변수 배열에 해당하는 명명된 인수 뒤인 경우 매개 변수에 해당하지 않습니다.
참고: 이렇게 하면
void M(bool a = true, bool b = true, bool c = true);가M(c: false, valueB);에 의해 호출되는 것이 방지됩니다. 첫 번째 인수는 out-of-position(인수는 첫 번째 위치에서 사용되지만c이라는 매개 변수는 세 번째 위치에 있음)이므로 다음 인수의 이름을 지정해야 합니다. 즉, 이름과 위치로 인해 동일한 해당 매개변수를 찾는 경우에만 후행되지 않는 명명된 인수가 허용됩니다. 끝 메모
12.6.2.3 인수 목록의 런타임 평가
함수 멤버 호출(§12.6.6)의 런타임 처리 중에 인수 목록의 식 또는 변수 참조는 다음과 같이 왼쪽에서 오른쪽으로 순서대로 계산됩니다.
값 인수의 경우 매개 변수의 전달 모드가 값인 경우
인수 식이 계산되고 해당 매개 변수 형식으로의 암시적 변환(§10.2)이 수행됩니다. 결과 값은 함수 멤버 호출에서 값 매개 변수의 초기 값이 됩니다.
그렇지 않으면 매개 변수의 전달 모드가 입력됩니다. 인수가 변수 참조이고 인수의 형식과 매개 변수 형식 사이에 ID 변환(§10.2.2)이 있는 경우 결과 스토리지 위치는 함수 멤버 호출에서 매개 변수가 나타내는 스토리지 위치가 됩니다. 그렇지 않으면 해당 매개 변수의 형식과 동일한 형식으로 스토리지 위치가 만들어집니다. 인수 식이 계산되고 해당 매개 변수 형식으로 암시적 변환(§10.2)이 수행됩니다. 결과 값은 해당 스토리지 위치 내에 저장됩니다. 해당 스토리지 위치는 함수 멤버 호출의 입력 매개 변수로 표시됩니다.
예제: 다음 선언 및 메서드 호출이 지정된 경우:
static void M1(in int p1) { ... } int i = 10; M1(i); // i is passed as an input argument M1(i + 5); // transformed to a temporary input argumentM1(i)메서드 호출에서i자체는 변수로 분류되고 입력 매개 변수와int형식이 동일하기 때문에 입력 인수로 전달됩니다.M1(i + 5)메서드 호출에서 명명되지 않은int변수가 만들어지고 인수 값으로 초기화된 다음 입력 인수로 전달됩니다. §12.6.4.2 및 §12.6.4.4참조하세요.예제 종료
입력, 출력 또는 참조 인수의 경우 변수 참조가 평가되고 결과 스토리지 위치는 함수 멤버 호출에서 매개 변수가 나타내는 스토리지 위치가 됩니다. 입력 또는 참조 인수의 경우 변수는 메서드 호출 지점에서 확실히 할당되어야 합니다. 변수 참조가 출력 인수로 지정되거나 reference_type배열 요소인 경우 배열의 요소 형식이 매개 변수 형식과 동일한지 확인하기 위해 런타임 검사가 수행됩니다. 이 검사가 실패하면
System.ArrayTypeMismatchException이 발생합니다.
참고: 배열 공변성(§17.6)으로 인해 이 런타임 검사가 필요합니다. 끝 메모
예제: 다음 코드에서
class Test { static void F(ref object x) {...} static void Main() { object[] a = new object[10]; object[] b = new string[10]; F(ref a[0]); // Ok F(ref b[1]); // ArrayTypeMismatchException } }
F의 두 번째 호출은System.ArrayTypeMismatchException의 실제 요소 유형이b가 아니고string이기 때문에object이 발생합니다.예제 종료
메서드, 인덱서 및 인스턴스 생성자는 가장 적합한 매개 변수를 매개 변수 배열로 선언할 수 있습니다(§15.6.2.4). 이러한 함수 멤버는 적용 가능한 형식에 따라 일반 형식 또는 확장된 형식으로 호출됩니다(§12.6.4.2).
- 매개 변수 배열이 있는 함수 멤버가 정규 형식으로 호출되면 매개 변수 배열에 지정된 인수는 매개 변수 배열 형식으로 암시적으로 변환할 수 있는 단일 식(§10.2)이어야 합니다. 이 경우 매개 변수 배열은 값 매개 변수처럼 정확하게 작동합니다.
- 매개 변수 배열이 있는 함수 멤버가 확장된 형식으로 호출되면 호출은 매개 변수 배열에 대해 0개 이상의 위치 인수를 지정해야 합니다. 여기서 각 인수는 매개 변수 배열의 요소 형식으로 암시적으로 변환할 수 있는 식(§10.2)입니다. 이 경우 호출은 인수 수에 해당하는 길이가 있는 매개 변수 배열 형식의 인스턴스를 만들고, 지정된 인수 값을 사용하여 배열 인스턴스의 요소를 초기화하고, 새로 만든 배열 인스턴스를 실제 인수로 사용합니다.
인수 목록의 식은 항상 작성된 순서대로 평가됩니다.
예제: 예를 들어, 이 예제
class Test { static void F(int x, int y = -1, int z = -2) => Console.WriteLine($"x = {x}, y = {y}, z = {z}"); static void Main() { int i = 0; F(i++, i++, i++); F(z: i++, x: i++); } }출력을 생성합니다.
x = 0, y = 1, z = 2 x = 4, y = -1, z = 3예제 종료
매개 변수 배열이 있는 함수 멤버가 확장된 인수를 하나 이상 사용하여 확장된 형식으로 호출되면 확장된 인수 주위에 배열 이니셜라이저(§12.8.17.4)가 있는 배열 생성 식이 삽입된 것처럼 호출이 처리됩니다. 매개 변수 배열에 대한 인수가 없으면 빈 배열이 전달됩니다. 전달된 참조가 새로 할당된 배열인지 아니면 기존 빈 배열에 대한 참조인지 지정되지 않습니다.
예제: 선언이 주어진 경우
void F(int x, int y, params object[] args);메서드의 확장된 형식에 대한 다음과 같은 호출
F(10, 20, 30, 40); F(10, 20, 1, "hello", 3.0);에 정확히 해당
F(10, 20, new object[] { 30, 40 }); F(10, 20, new object[] { 1, "hello", 3.0 });예제 종료
해당 선택적 매개 변수가 있는 함수 멤버에서 인수를 생략하면 함수 멤버 선언의 기본 인수가 암시적으로 전달됩니다. (위에서 설명한 대로 스토리지 위치를 만드는 작업이 포함될 수 있습니다.)
참고: 항상 상수이므로 해당 계산은 나머지 매개변수의 계산에 영향을 주지 않습니다. 끝 메모
12.6.3 형식 유추
12.6.3.1 일반
제네릭 메서드가 형식 인수를 지정하지 않고 호출되면 형식 유추 프로세스에서 호출에 대한 형식 인수를 유추하려고 시도합니다. 형식 유추가 있으면 제네릭 메서드를 호출하는 데 보다 편리한 구문을 사용할 수 있으며 프로그래머가 중복 형식 정보를 지정하지 않도록 할 수 있습니다.
예제:
class Chooser { static Random rand = new Random(); public static T Choose<T>(T first, T second) => rand.Next(2) == 0 ? first : second; } class A { static void M() { int i = Chooser.Choose(5, 213); // Calls Choose<int> string s = Chooser.Choose("apple", "banana"); // Calls Choose<string> } }형식 추론을 통해 메서드의 인수로부터
int및string형식 인수가 결정됩니다.예제 종료
형식 유추는 메서드 호출(§12.8.10.2)의 바인딩 시간 처리의 일부로 발생하며 호출의 오버로드 확인 단계 전에 발생합니다. 메서드 호출에서 특정 메서드 그룹을 지정하고 메서드 호출의 일부로 형식 인수를 지정하지 않으면 메서드 그룹의 각 제네릭 메서드에 형식 유추가 적용됩니다. 형식 유추에 성공하면 유추된 형식 인수를 사용하여 이후 오버로드 해석의 인수 형식을 결정합니다. 오버로드 해제에서 제네릭 메서드가 호출될 메서드로 선택되면, 유추된 형식 인수는 호출 시 형식 인수로 사용됩니다. 특정 메서드에 대한 형식 유추가 실패하면 해당 메서드는 오버로드 확인에 참여하지 않습니다. 형식 유추 자체가 실패해도 바인딩 시간 오류가 발생하지는 않습니다. 오버로드 해석이 적용 가능한 메서드를 찾지 못할 때 바인딩 시간 오류가 발생하는 경우가 많습니다.
제공된 각 인수가 메서드에서 정확히 하나의 매개 변수(§12.6.2.2)에 해당하지 않거나 해당 인수가 없는 선택적 매개 변수가 없는 경우 유추가 즉시 실패합니다. 그렇지 않으면 제네릭 메서드에 다음 서명이 있다고 가정합니다.
Tₑ M<X₁...Xᵥ>(T₁ p₁ ... Tₓ pₓ)
형식 유추 작업은 M(E₁ ...Eₓ) 형태의 메서드 호출에서, 각 형식 매개 변수 S₁...Sᵥ에 대해 호출 X₁...Xᵥ가 유효해지도록 하는 고유한 형식 인수 M<S₁...Sᵥ>(E₁...Eₓ)을 찾는 것입니다.
형식 유추 프로세스는 아래에 알고리즘으로 설명되어 있습니다. 모든 경우에 동일한 결과에 도달하면 호환되는 컴파일러가 대체 방법을 사용하여 구현될 수 있습니다.
유추 과정에서 각 형식 매개 변수 Xᵢ는 특정 형식 에 Sᵢ되거나, 연결된 범위 집합과 함께 고정되지 않은 상태입니다. 각 범위는 어떤 T형식입니다. 처음에는 각 형식 변수 Xᵢ는 빈 범위 집합으로 설정되지 않은 상태입니다.
형식 유추는 단계적으로 발생합니다. 각 단계에서는 이전 단계의 결과에 따라 더 많은 형식 변수에 대한 형식 인수를 유추하려고 합니다. 첫 번째 단계에서는 범위의 몇 가지 초기 유추를 만드는 반면, 두 번째 단계에서는 형식 변수를 특정 형식으로 수정하고 더 많은 경계를 유추합니다. 두 번째 단계는 여러 번 반복해야 할 수 있습니다.
참고: 형식 유추는 메서드 그룹 변환(§12.6.3.15) 및 식 집합의 가장 일반적인 형식 찾기(§12.6.3.16)를 비롯한 다른 컨텍스트에서도 사용됩니다. 끝 메모
12.6.3.2 첫 번째 단계
각 메서드 인수Eᵢ에 대해 입력 형식 유추(§12.6.3.7)를 해당 매개 변수 형식Eᵢ으로 Tⱼ 만듭니다.
12.6.3.3 두 번째 단계
두 번째 단계는 다음과 같이 진행됩니다.
- (§12.6.3.6)
Xᵢ에 의존하지 않는 모든 고정되지 않은 형식 변수Xₑ는 고정됩니다(§12.6.3.13). - 이러한 형식 변수가 존재하지 않으면, 다음 조건을 모두 충족하는 모든
고정되지 않은 형식 변수가고정됩니다 .-
Xₑ에 의존하는 하나 이상의 형식 변수Xᵢ가 있습니다. -
Xᵢ에는 비어 있지 않은 경계 집합이 있습니다.
-
- 이러한 형식 변수가 없고 아직 고정되지 않은 형식 변수가 있는 경우 형식 유추가 실패합니다.
- 그렇지 않으면 추가 고정되지 않은 형식 변수가 없으면 형식 유추가 성공합니다.
- 그렇지 않으면
EᵢTⱼ(§12.6.3.5)에 고정되지 않은 형식 변수Xₑ가 포함되지만 입력 형식(§12.6.3.4)이 포함되지 않는 해당 매개 변수 형식의 모든 인수 에 대해 출력 형식 유추(§12.6.3.8)가EᵢTⱼ그런 다음 두 번째 단계가 반복됩니다.
12.6.3.4 입력 형식
12.6.3.5 출력 형식
12.6.3.6 의존성
Xₑ
는 XᵢXₑ에 직접 의존하거나, XᵢXᵢ에 직접 의존하면서 XᵥXᵥ에 의존하는 경우에 따라Xₑ에 달려 있습니다. 따라서 "의존합니다"는 "직접 의존합니다"의 전이적이지만 반사적이지 않은 폐쇄입니다.
12.6.3.7 입력 형식 유추
입력 형식 유추는 다음과 같은 방식으로 식 E 에서
- arity 및 요소가
E있는 튜플 식(§12.8.6)이고N해당 요소 형식이 있는 arityEᵢ가 있는 튜플 형식T이거나Nnull 허용 값 형식Tₑ이고T해당 요소 형식T0?이 있는 튜플 형식T0인 경우N각각Tₑ에 대해 입력 형식 유추가 만들어집니다EᵢEᵢ.Tₑ - 무
E명 함수인 경우명시적 매개 변수 형식 유추(E)가T - 그렇지 않은 경우 형식
EU이 있고 해당 매개 변수가 값 매개 변수(§15.6.2.2)이면 하한 유추(§12.6.3.11)가UT - 그렇지 않은 경우
E형식U이 있고 해당 매개 변수가 참조 매개 변수(§15.6.2.3.3) 또는 출력 매개 변수(§15.6.2.3.4)인 경우 정확한 유추(§12.6.3.10)가UT - 그렇지 않은 경우 형식
EU이 있고 해당 매개 변수가 입력 매개 변수(§15.6.2.3.2)이고E입력 인수인 경우 정확한 유추(§12.6.3.10)가UT - 그렇지 않은 경우 형식
EU이 있고 해당 매개 변수가 입력 매개 변수(§15.6.2.3.2)이면 하한 유추(§12.6.3.11)가UT - 그렇지 않으면 이 인수에 대한 추론이 수행되지 않습니다.
12.6.3.8 출력 형식 유추
출력 형식 유추는 다음과 같은 방식으로 식 E 에서
- arity 및 요소가
E있는 튜플 식이고N해당 요소 형식Eᵢ이 있는 arityT가 있는 튜플 형식이거나Nnull 허용 값 형식이고Tₑ해당 요소 형식T이 있는 튜플 형식T0?T0인 경우N각Tₑ출력 형식 유추가 만들어집니다EᵢEᵢ.Tₑ - 유추
E된 반환 형식U(§12.6.3.14)이 있는 무명 함수이고T반환 형식이 있는 대리자 형식 또는 식 트리 형식Tₓ인 경우 하한 유추(§12.6.3.11)가UTₓ - 그렇지 않으면,
이 메서드 그룹이고 이 매개 변수 형식 와 반환 형식 을 가진 대리자 형식이나 식 트리 형식이며, 형식 의 를 오버로드 해결하여 반환 형식이 인 단일 메서드를 산출한다면, 에서 로 하한 유추 가 이루어집니다. - 그렇지 않으면,
이 유형의 식이라면, 에서 으로 하한 유추 이됩니다. - 그렇지 않으면 유추가 이루어지지 않습니다.
12.6.3.9 명시적 매개 변수 형식 유추
명시적 매개변수 유형 유추는 식 E유형T으로 다음과 같은 방법으로 수행됩니다.
- 매개 변수 형식
E이 있는 명시적으로 형식화된 무명 함수이고U₁...UᵥT매개 변수V₁...Vᵥ형식이 있는 대리자 형식 또는 식 트리 형식인 경우Uᵢ각각 에 대해 정확한 유추(§12.6.3.10)가 해당Uᵢ함수로Vᵢ만들어집니다.
12.6.3.10 정확한 유추
형식에서 형식으로 U를 다음과 같이 수행합니다.
-
V고정되지 않은Xᵢ중 하나인 경우UXᵢ대한 정확한 범위 집합에 추가됩니다. - 그렇지 않으면,
V₁...Vₑ집합과U₁...Uₑ집합은 다음 경우 중 하나가 적용되는지를 확인하여 결정됩니다.-
VV₁[...]배열 형식이며U동일한 순위의U₁[...]배열 형식입니다. -
V는V₁?형식이고U는U₁형식입니다. -
V는 생성된 형식C<V₁...Vₑ>이고U는 생성된 형식C<U₁...Uₑ>입니다.
이러한 사례가 적용되는 경우 각각 에서 해당하는Uᵢ로Vᵢ가 이루어집니다.
-
- 그렇지 않으면 유추가 이루어지지 않습니다.
12.6.3.11 하한 유추
형식
-
V고정되지 않은Xᵢ중 하나인 경우UXᵢ하한 집합에 추가됩니다. - 그렇지 않으면
V이(가)V₁?타입이고U이(가)U₁?타입이라면,U₁에서V₁로 하한 유추가 이루어집니다. - 그렇지 않으면,
U₁...Uₑ집합과V₁...Vₑ집합은 다음 경우 중 하나가 적용되는지를 확인하여 결정됩니다.-
V는 배열 타입V₁[...]이고,U는 동일한 순위의 배열 타입U₁[...]입니다. -
V는IEnumerable<V₁>,ICollection<V₁>,IReadOnlyList<V₁>>,IReadOnlyCollection<V₁>또는IList<V₁>중 하나이며,U는 단일 차원 배열 형식인U₁[]입니다. -
V은(는) 생성된class,struct,interface또는delegate유형C<V₁...Vₑ>이며, 고유한 유형인C<U₁...Uₑ>이(가) 존재하여U(또는U이(가)parameter유형인 경우, 해당 유효 기본 클래스 또는 유효한 인터페이스 집합의 멤버)이inherits와(과) 동일하거나 (직접 또는 간접적으로) 상속하거나 (직접 또는 간접적으로)C<U₁...Uₑ>을(를) 구현합니다. - "‘고유성’ 제한은 인터페이스
C<T>{} class U: C<X>, C<Y>{}의 경우에 적용되며, 이때U이C<T>또는U₁일 수 있기 때문에X에서Y로 유추할 때 유추가 이루어지지 않는다는 것을 의미합니다."
이러한 사례가 적용되는 경우, 다음과 같이 각Uᵢ부터 해당Vᵢ로 대한 유추가 수행됩니다. -
Uᵢ참조 형식으로 알려져 있지 않으면 정확한 유추 만들어집니다. - 그렇지 않으면
U배열 형식이면 하한 유추 만들어집니다. - 그렇지 않고,
V이C<V₁...Vₑ>인 경우 유추는i-th의C유형 매개 변수에 따라 달라집니다.- 공변성인 경우 하한 유추 만들어집니다.
- 반공변성인 경우 상한 유추 가 이루어집니다.
- 고정된 경우 정확한 유추 가 이루어집니다.
-
- 그렇지 않으면 유추가 이루어지지 않습니다.
12.6.3.12 상한 유추
형식 상한 유추는 형식 U에서 형식 으로 다음과 같이 이루어집니다.
-
V고정되지 않은Xᵢ중 하나인 경우UXᵢ대한 상한 집합에 추가됩니다. - 그렇지 않으면,
V₁...Vₑ집합과U₁...Uₑ집합은 다음 경우 중 하나가 적용되는지를 확인하여 결정됩니다.-
U는 배열 타입U₁[...]이고,V는 동일한 순위의 배열 타입V₁[...]입니다. -
U는IEnumerable<Uₑ>,ICollection<Uₑ>,IReadOnlyList<Uₑ>,IReadOnlyCollection<Uₑ>또는IList<Uₑ>중 하나이며,V는 단일 차원 배열 형식인Vₑ[]입니다. -
U는U1?형식이고V는V1?형식입니다. -
U은(는) 클래스, 구조체, 인터페이스 또는 대리자 형식으로 구성되고C<U₁...Uₑ>과V은(는)class, struct, interface또는delegate형식이며, 이는 직간접적으로identical고유 형식과inherits로 관계되거나C<V₁...Vₑ>로부터 관계되거나, (직간접적으로) 구현합니다. - "‘고유성’ 제한은 인터페이스가
C<T>{} class V<Z>: C<X<Z>>, C<Y<Z>>{}인 경우,C<U₁>에서V<Q>로 유추가 이루어지지 않음을 의미합니다. 또한,U₁에서X<Q>나Y<Q>로 유추가 이루어지지 않습니다."
이러한 사례가 적용되는 경우, 다음과 같이 각Uᵢ부터 해당Vᵢ로 대한 유추가 수행됩니다. -
Uᵢ참조 형식으로 알려져 있지 않으면 정확한 유추 만들어집니다. - 그렇지 않은 경우,
V가 배열 형식이면 상한 유추가에서 이루어집니다. - 그렇지 않고,
U이C<U₁...Uₑ>인 경우 유추는i-th의C유형 매개 변수에 따라 달라집니다.- 공변성인 경우 상한 유추 만들어집니다.
- 반공변성인 경우, 하한 유추이 이루어집니다.
- 고정된 경우 정확한 유추 가 이루어집니다.
-
- 그렇지 않으면 유추가 이루어지지 않습니다.
12.6.3.13 수정
범위 집합이 있는 고정되지 않은 형식 변수 Xᵢ는 다음과 같은 방식으로 고정됩니다.
후보 유형 집합은 의 범위에 있는 모든 유형의 집합으로 시작합니다. - 각
Xᵢ의 경계는 차례대로 검토됩니다.Xᵢ의 모든 정확한 경계 U에 대해,Uₑ와 동일하지 않은 모든 유형U가 후보 집합에서 제거됩니다.U각 하한Xᵢ의 경우,Uₑ이 로부터 이루어지지 않는 모든U유형이 후보 집합에서 제거됩니다.Xᵢ각 상한 U에 대해Uₑ에서 로의 암시적 변환이 없는 모든 형식은 후보 집합에서 제거됩니다. - 나머지 후보 유형 중
Uₑ에서, 다른 모든 후보 형식으로부터 암시적 변환이 가능한 고유한 유형V이 있는 경우,Xᵢ를V으로 고정합니다. - 그렇지 않으면 형식 유추가 실패합니다.
12.6.3.14 유추 반환 형식
익명 함수 F 유추된 반환 형식은 형식 유추 및 오버로드 확인 중에 사용됩니다. 유추된 반환 형식은 모든 매개 변수 형식이 알려진 익명 함수에 대해서만 확인할 수 있습니다. 이는 매개 변수 형식이 명시적으로 제공되거나, 익명 함수 변환을 통해 제공되거나, 혹은 바깥쪽 제네릭 메서드 호출 시의 형식 유추 과정에서 유추된 경우 가능합니다.
유추된 유효 반환 형식 다음과 같이 결정됩니다.
-
F본문이 형식이 있는 식 경우, 유추된 유효한F반환 형식은 해당 식의 형식입니다. - 본문
F이 블록 이고 블록return문의 식 집합이 가장 일반적인 형식T(§12.6.3.16)인 경우 유추된 유효 반환 형식F은 다음과 같습니다T. - 그렇지 않으면
F의 유효한 반환 형식을 유추할 수 없습니다.
유추된 반환 형식 다음과 같이 결정됩니다.
-
F가 비동기일 때,F본문이 nothing으로 분류된 식(§12.2)이거나, 문에 식이 없는 블록일 경우, 유추된 반환 형식은return입니다(«TaskType»). - 비동기이고 유추된 유효 반환 형식
F이 있는 경우T유추된 반환 형식은 («TaskType»<T>»)입니다. -
F비동기이고 유추된 유효 반환 형식이T경우 유추된 반환 형식은T. - 그렇지 않으면
F의 반환 형식을 유추할 수 없게 됩니다.
예제: 무명 함수와 관련된 형식 유추의 예로
Select클래스에 선언된System.Linq.Enumerable확장 메서드를 고려합니다.namespace System.Linq { public static class Enumerable { public static IEnumerable<TResult> Select<TSource,TResult>( this IEnumerable<TSource> source, Func<TSource,TResult> selector) { foreach (TSource element in source) { yield return selector(element); } } } }
System.Linq네임스페이스가using namespace지시문으로 가져와졌고,Customer형식의Name속성을 가진 클래스string가 주어진 경우,Select메서드를 사용하여 고객 목록의 이름을 선택할 수 있습니다.List<Customer> customers = GetCustomerList(); IEnumerable<string> names = customers.Select(c => c.Name);확장 메서드 호출(
Select)은 정적 메서드 호출로 다시 작성되어 처리된다.IEnumerable<string> names = Enumerable.Select(customers, c => c.Name);형식 인수가 명시적으로 지정되지 않았기 때문에 형식 유추를 사용하여 형식 인수를 유추합니다. 먼저, 고객의 인수는 원본 매개 변수와 관련이 있으며
TSource를Customer로 유추합니다. 그런 다음 위에서 설명한 익명 함수 유형 추론 과정을 사용하여c에Customer유형을 부여하고, 식c.Name는 선택기 매개 변수의 반환 유형과 관련이 있어TResult을string로 추론합니다. 따라서 호출은 다음과 동일합니다Sequence.Select<Customer,string>(customers, (Customer c) => c.Name)결과는
IEnumerable<string>형식입니다.다음 예제에서는 익명 함수 타입 유추를 통해 제네릭 메서드 호출의 인수 간에 형식 정보가 전달되는 방법을 보여 줍니다. 다음 메서드 및 호출이 제공된 경우:
class A { static Z F<X,Y,Z>(X value, Func<X,Y> f1, Func<Y,Z> f2) { return f2(f1(value)); } static void M() { double hours = F("1:15:30", s => TimeSpan.Parse(s), t => t.TotalHours); } }호출에 대한 형식 유추는 다음과 같이 진행됩니다: 첫째, "1:15:30" 인수는 값 매개 변수와 연관되어
X를string로 유추합니다. 이어서, 첫 번째 익명 함수의 매개변수s은 유추된 형식string을 갖게 되며, 식TimeSpan.Parse(s)는f1의 반환 형식과 관련됩니다. 따라서Y는System.TimeSpan로 유추됩니다. 마지막으로, 두 번째 익명 함수의 매개변수t에는 유추된 형식System.TimeSpan이 지정되며, 식t.TotalHours는f2의 반환 형식과 관련이 있어Z가double으로 유추됩니다. 따라서 호출 결과는double형식입니다.예제 종료
12.6.3.15 메서드 그룹의 변환에 대한 형식 유추
제네릭 메서드의 호출과 마찬가지로 제네릭 메서드를 포함하는 메서드 그룹 M 지정된 대리자 형식 D(§10.8)로 변환될 때도 형식 유추가 적용됩니다. 지정된 메서드
Tₑ M<X₁...Xᵥ>(T₁ x₁ ... Tₑ xₑ)
형식 유추의 과제는 M 메서드 그룹이 D 대리자 형식에 할당되어 다음과 같은 식에서 S₁...Sᵥ 형식 인수를 찾는 것입니다.
M<S₁...Sᵥ>
와 호환됩니다(§21.2) D.
제네릭 메서드 호출에 대한 형식 유추 알고리즘과 달리 이 경우 인수 형식만, 인수 식은없습니다. 특히 익명 함수는 없으므로 여러 단계의 유추가 필요하지 않습니다.
대신, 모든 Xᵢ은 미고정으로 간주되며, 의 각 인수 형식UₑD의 해당 매개 변수 형식Tₑ가 이루어집니다.
Xᵢ의 경계를 찾을 수 없으면 형식 유추가 실패합니다. 그렇지 않으면 모든
12.6.3.16 식 집합의 가장 일반적인 형식 찾기
어떤 경우에는 식 집합에 대해 공통 유형을 유추해야 합니다. 특히 암시적으로 형식화된 배열의 요소 형식과 블록 본문이 있는 익명 함수의 반환 형식이 이러한 방식으로 발견됩니다.
E₁...Eᵥ 식 집합에 가장 적합한 공통 형식은 다음과 같이 결정됩니다.
- 새로운 고정되지 않은 형식 변수
X도입되었습니다. - 각 식
Ei에 대해 출력 형식 유추 (§12.6.3.8)가 출력 형식 유추에서X수행됩니다. -
X가 수정 되고(가능한 경우§12.6.3.13), 결과 형식이 가장 일반적인 형식입니다. - 그렇지 않으면 유추가 실패합니다.
참고: 직관적으로 이 유추는 메서드
void M<X>(X x₁ ... X xᵥ)를 호출하고Eᵢ를 인수로 사용하여X를 유추하는 것과 같습니다. 끝 메모
12.6.4 오버로드 해결
12.6.4.1 일반
오버로드 확인은 인수 목록 및 후보 함수 멤버 집합이 지정된 경우 호출할 가장 적합한 함수 멤버를 선택하는 바인딩 시간 메커니즘입니다. 오버로드 확인은 C#에서 다음과 같은 고유한 컨텍스트에서 호출할 함수 멤버를 선택합니다.
- 명명된 메서드의 호출(invocation_expression,§12.8.10).
- object_creation_expression에 명명된 인스턴스 생성자 호출 (§12.8.17.2).
- 인덱서 접근자는 element_access 을 통해 호출됩니다(§12.8.12).
- 식에서 참조된 미리 정의된 연산자 또는 사용자 정의 연산자의 호출(§12.4.4 및 §12.4.5).
이러한 각 컨텍스트는 고유한 방식으로 후보 함수 멤버 집합과 인수 목록을 정의합니다. 예를 들어 메서드 호출에 대한 후보 집합에는 재정의(§12.5)로 표시된 메서드가 포함되지 않으며 파생 클래스의 메서드가 적용 가능한 경우 기본 클래스의 메서드는 후보가 아닙니다(§12.8.10.2).
후보 함수 멤버와 인수 목록이 식별되면 최상의 함수 멤버의 선택은 모든 경우에 동일합니다.
- 첫째, 후보 함수 멤버 집합은 지정된 인수 목록(§12.6.4.2)과 관련하여 적용 가능한 함수 멤버로 축소됩니다. 이 축소된 집합이 비어 있으면 컴파일 시간 오류가 발생합니다.
- 그런 다음, 적용 가능한 후보 함수 멤버 집합에서 가장 적합한 함수 멤버를 찾습니다. 집합에 하나의 함수 멤버만 포함된 경우 해당 함수 멤버가 가장 적합한 함수 멤버입니다. 그렇지 않으면 각 함수 멤버가 §12.6.4.3규칙을 사용하여 다른 모든 함수 멤버와 비교되는 경우 지정된 인수 목록과 관련하여 다른 모든 함수 멤버보다 더 나은 함수 멤버가 가장 좋습니다. 다른 모든 함수 멤버보다 더 나은 함수 멤버가 하나도 없는 경우 함수 멤버 호출이 모호하고 바인딩 시간 오류가 발생합니다.
다음 하위 조항에서는 해당 함수 멤버 및 더 나은 함수 멤버용어의 정확한 의미를 정의합니다.
12.6.4.2 적용 가능한 함수 멤버
함수 멤버가 인수 목록
-
A각 인수는 §12.6.2.2설명된 대로 함수 멤버 선언의 매개 변수에 해당하며, 최대 하나의 인수는 각 매개 변수에 해당하며 인수가 없는 매개 변수는 선택적 매개 변수입니다. -
A각 인수에 대해 인수의 매개 변수 전달 모드는 해당 매개 변수의 매개 변수 전달 모드와 동일하며,
매개 변수 배열을 포함하는 함수 멤버의 경우, 위의 규칙에 따라 함수 멤버가 적용 가능한 경우 의 정규 형식이 적용 가능하다고 합니다. 매개 변수 배열을 포함하는 함수 멤버를 일반 형식으로 적용할 수 없는 경우 함수 멤버는
- 확장된 폼은 인수 목록의 인수 수가 총 매개 변수 수와 일치할
A있도록 함수 멤버 선언의 매개 변수 배열을 매개 변수 배열의 요소 형식에 대한 0개 이상의 값 매개 변수로 바꿔서 생성됩니다.A함수 멤버 선언의 고정 매개 변수 수보다 인수 수가 적으면 함수 멤버의 확장된 형식을 생성할 수 없으므로 적용할 수 없습니다. - 그렇지 않으면
A각 인수에 대해 다음 중 하나가 true인 경우 확장된 양식이 적용됩니다.
인수 형식에서 입력 매개 변수의 매개 변수 형식으로의 암시적 변환이 동적 암시적 변환(§10.2.10)이면 결과가 정의되지 않습니다.
예제: 다음 선언 및 메서드 호출이 지정된 경우:
public static void M1(int p1) { ... } public static void M1(in int p1) { ... } public static void M2(in int p1) { ... } public static void Test() { int i = 10; uint ui = 34U; M1(in i); // M1(in int) is applicable M1(in ui); // no exact type match, so M1(in int) is not applicable M1(i); // M1(int) and M1(in int) are applicable M1(i + 5); // M1(int) and M1(in int) are applicable M1(100u); // no implicit conversion exists, so M1(int) is not applicable M2(in i); // M2(in int) is applicable M2(i); // M2(in int) is applicable M2(i + 5); // M2(in int) is applicable }예제 종료
- 정적 메서드는 메서드 그룹이 형식을 통해 simple_name 또는 member_access의 결과로 나올 때만 적용할 수 있습니다.
- 인스턴스 메서드는 메서드 그룹이 simple_name, 변수 또는 값을 통한 member_access, 또는 base_access에서 나온 경우에만 적용됩니다.
- 메서드 그룹이
simple_name 에서 생성된 경우, 인스턴스 메서드는 §12.8.14에서 액세스가 허용될 때에만 적용됩니다.
- 메서드 그룹이
- 메서드 그룹이 member_access의 결과인 경우, 이는 §12.8.7.2에 설명된 바와 같이 인스턴스나 형식을 통해 이루어질 수 있으며, 인스턴스 메서드와 정적 메서드 모두 적용 가능합니다.
- 형식 인수(명시적으로 지정되거나 유추됨)가 해당 제약 조건을 모두 충족하지 않는 제네릭 메서드는 적용할 수 없습니다.
- 메서드 그룹 변환의 컨텍스트에서 메서드 반환 형식에서 대리자의 반환 형식으로의 ID 변환(§10.2.2) 또는 암시적 참조 변환(§10.2.8)이 있어야 합니다. 그렇지 않으면 후보 메서드를 적용할 수 없습니다.
12.6.4.3 더 나은 함수 멤버
더 나은 함수 멤버를 결정하기 위해 A 제거된 인수 목록은 원래 인수 목록에 나타나는 순서대로 인수 식만 포함하고 out 또는 ref 인수를 제외하여 생성됩니다.
각 후보 함수 멤버에 대한 매개 변수 목록은 다음과 같은 방식으로 생성됩니다.
- 확장된 폼은 확장된 폼에서만 함수 멤버를 적용할 수 있는 경우에 사용됩니다.
- 해당 인수가 없는 선택적 매개 변수는 매개 변수 목록에서 제거됩니다.
- 매개 변수 목록에서 참조 및 출력 매개 변수가 제거됨
- 매개 변수는 인수 목록의 해당 인수와 동일한 위치에서 수행되도록 순서가 다시 지정됩니다.
인수 목록 A가 주어졌을 때, 인수 식 집합 {E₁, E₂, ..., Eᵥ}과 두 개의 적용 가능한 함수 멤버 Mᵥ와 Mₓ가 매개 변수 형식 {P₁, P₂, ..., Pᵥ} 및 {Q₁, Q₂, ..., Qᵥ}를 가지며, Mᵥ가 보다 정의됩니다.
- 각 인수에 대해,
Eᵥ에서Qᵥ로의 암시적 변환은Eᵥ에서Pᵥ로의 암시적 변환보다 더 나은 것이 아닙니다. - 인수가 하나 이상인 경우
EᵥPᵥ변환하는 것이EᵥQᵥ변환보다 낫습니다.
매개변수 타입 시퀀스 {P₁, P₂, ..., Pᵥ}과 {Q₁, Q₂, ..., Qᵥ}이 서로 등가인 경우(각 Pᵢ가 해당 Qᵢ로의 동일 변환을 가지는 경우), 더 우수한 함수 멤버를 결정하기 위해 다음의 동점 처리 규칙이 순서대로 적용됩니다.
-
Mᵢ제네릭이 아닌 메서드이고Mₑ제네릭 메서드인 경우MᵢMₑ것보다 낫습니다. - 그렇지 않으면
Mᵢ일반 형식에 적용할 수 있고Mₑ매개 변수 배열이 있고 확장된 형식에서만 적용할 수 있는 경우MᵢMₑ것보다 낫습니다. - 그렇지 않은 경우 두 메서드에 매개 변수 배열이 있고 확장된 양식에서만 적용할 수 있고
Mᵢ매개 변수 배열에Mₑ매개 변수 배열보다 적은 요소가 있는 경우MᵢMₑ것보다 낫습니다. - 그렇지 않으면
MᵥMₓ보다 더 구체적인 매개 변수 형식이 있는 경우MᵥMₓ보다 낫습니다.{R1, R2, ..., Rn}및{S1, S2, ..., Sn}은Mᵥ및Mₓ의 인스턴스화되지 않고 확장되지 않은 매개변수 유형을 나타냅니다.Mᵥ매개 변수 형식은 각 매개 변수에 대해MₓRx보다 덜 구체적이지 않고 하나 이상의 매개 변수에 대해SxRx보다 구체적인 경우Sx보다 더 구체적입니다.- 형식 매개 변수는 형식이 아닌 매개 변수보다 덜 구체적입니다.
- 재귀적으로, 하나의 생성된 형식이 다른 생성된 형식(동일한 수의 형식 인수 포함)보다 더 구체적인 것은, 적어도 하나의 형식 인수가 그 대응하는 형식 인수보다 더 구체적이고, 어떤 형식 인수도 덜 구체적이지 않을 때입니다.
- 첫 번째 요소 형식이 두 번째 요소 형식보다 더 구체적인 경우 배열 형식은 다른 배열 형식(차원 수가 같음)보다 더 구체적입니다.
- 그렇지 않으면 한 멤버가 해제되지 않은 연산자이고 다른 멤버가 리프트된 연산자인 경우 해제되지 않은 멤버가 더 좋습니다.
- 만약 두 함수 멤버가 더 나은 것으로 판정되지 않았고, 모든 매개 변수에 대해
Mᵥ의 해당 인수가 있는 반면,Mₓ에 있는 최소한 하나의 선택적 매개 변수는 기본 인수로 대체되어야 한다면,Mᵥ가Mₓ보다 낫습니다. - 하나 이상의 매개변수
Mᵥ가 의 해당 매개변수보다 더 나은 매개변수 전달 선택(§12.6.4.4)을 사용하고,Mₓ의 매개변수 중 어느 것도Mₓ보다 더 나은 매개변수 전달 선택을 사용하지 않는 경우,Mᵥ는Mᵥ보다 낫습니다. - 그렇지 않다면 어떤 함수 멤버도 더 낫다고 할 수 없습니다.
12.6.4.4 매개 변수 전달 모드 개선
다음과 같이 두 매개 변수 중 하나에 값 전달 모드가 있는 경우 두 오버로드된 메서드의 해당 매개 변수는 매개 변수 전달 모드에 따라 다를 수 있습니다.
public static void M1(int p1) { ... }
public static void M1(in int p1) { ... }
int i = 10;의 경우, §12.6.4.2에 따라 호출 M1(i)과 M1(i + 5)는 두 오버로드가 모두 적용 가능하게 만듭니다. 이러한 경우, 값 매개변수 전달 모드를 사용하는 메서드는 더 나은 매개변수 전달 모드 선택입니다.
참고: 이러한 인수는 정확히 동일한 매개 변수 전달 모드와 일치하기 때문에 입력, 출력 또는 참조 전달 모드의 인수에는 이러한 선택이 필요하지 않습니다. 끝 메모
12.6.4.5 식에서 더 나은 변환
암시적 변환 C₁은(는) 식 E을(를) 형식 T₁로 변환하고, 암시적 변환 C₂은(는) 식 E을(를) 형식 T₂로 변환하는 경우, 다음 중 하나에 해당하면 C₁이(가) 보다 입니다.
-
E정확히T₁일치하고E정확히T₂일치하지 않습니다 (§12.6.4.6) -
E는 정확히T₁및T₂둘 다와 일치하거나 둘 다와 일치하지 않으며,T₁는T₂(§12.6.4.7)보다 더 나은 변환 대상입니다. -
E는 메서드 그룹(§12.2)T₁이고, 변환을 위해 메서드 그룹의 단일 최상의 메서드와 호환되며(§21.4) 변환C₁을 위해 메서드 그룹의 단일 최상의 메서드와T₂호환되지 않습니다.C₂
12.6.4.6 정확히 일치하는 식
주어진 수식 E 및 형식 T의 경우, 다음 중 하나가 적용되는 경우 E는T과 정확히 일치합니다.
-
E는S형식이며,S에서T으로의 동일 변환이 존재합니다. -
E은 익명 함수입니다.T은 대리자 형식D또는 식 트리 형식Expression<D>이며, 다음 조건 중 하나를 충족합니다.- 매개 변수 목록(
X)의E컨텍스트에 유추된 반환 형식D이 있으며 ID 변환이 의 반환 형식으로X존재합니다.D -
E은 반환 값이 없는async람다이며,D은 반환 형식이 제네릭이 아닌«TaskType»입니다. -
E이 비동기형식이 아니고D이 반환 형식Y을 가지거나,E이 비동기형식이고D이 반환 형식«TaskType»<Y>(§15.14.1)을 가질 때 다음 중 하나를 충족합니다.-
E본문은Y과 정확히 일치하는 표현입니다. -
E의 본문은 모든 return 문이 정확히 일치하는 식을 반환하는 블록입니다Y
-
- 매개 변수 목록(
12.6.4.7 더 나은 변환 대상
두 가지 형식 T₁ 및 T₂이 주어질 때, 만약 다음 중 하나가 성립하면 T₁는 보다 더 나은 변환 대상 입니다.
-
T₁에서T₂로의 암시적 변환은 존재하고,T₂에서T₁로의 암시적 변환은 존재하지 않습니다. -
T₁은«TaskType»<S₁>(§15.14.1),T₂은«TaskType»<S₂>이며,S₁은S₂보다 더 나은 변환 대상입니다. -
T₁은«TaskType»<S₁>(§15.14.1),T₂은«TaskType»<S₂>이고,T₁은T₂보다 더 특화되어 있습니다. -
T₁는S₁또는S₁?이며,S₁는 부호 있는 정수 계열 형식입니다. 또한,T₂는S₂또는S₂?이고S₂은 부호 없는 정수 계열 형식입니다. 특히:-
S₁는sbyte이며S₂는byte,ushort,uint또는ulong입니다. -
S₁은short이며S₂는ushort,uint또는ulong입니다. -
S₁는int이고S₂는uint또는ulong -
S₁는long이며S₂는ulong이다
-
12.6.4.8 제네릭 클래스의 오버로드
참고: 선언된 서명은 고유해야 하지만(§8.6), 형식 인수를 대체하면 동일한 서명이 생성될 수 있습니다. 이러한 상황에서 오버로드 해석은 형식 인수를 대체하기 전의 원래 시그니처 중 가장 구체적인 것(§12.6.4.3)이 존재할 경우 이를 선택하며, 그렇지 않으면 오류를 발생시킵니다. 끝 메모
예제: 다음 예제에서는 이 규칙에 따라 유효하고 유효하지 않은 오버로드를 보여 줍니다.
public interface I1<T> { ... } public interface I2<T> { ... } public abstract class G1<U> { public abstract int F1(U u); // Overload resolution for G<int>.F1 public abstract int F1(int i); // will pick non-generic public abstract void F2(I1<U> a); // Valid overload public abstract void F2(I2<U> a); } abstract class G2<U,V> { public abstract void F3(U u, V v); // Valid, but overload resolution for public abstract void F3(V v, U u); // G2<int,int>.F3 will fail public abstract void F4(U u, I1<V> v); // Valid, but overload resolution for public abstract void F4(I1<V> v, U u); // G2<I1<int>,int>.F4 will fail public abstract void F5(U u1, I1<V> v2); // Valid overload public abstract void F5(V v1, U u2); public abstract void F6(ref U u); // Valid overload public abstract void F6(out V v); }예제 종료
12.6.5 동적 멤버 호출의 컴파일 시간 검사
동적으로 바인딩된 작업의 오버로드 확인은 런타임에 수행되지만 컴파일 시간에 오버로드가 선택될 함수 멤버 목록을 알 수 있는 경우도 있습니다.
- 대리자 호출(§12.8.10.4)의 경우 목록은 호출 delegate_type 매개 변수 목록이 동일한 단일 함수 멤버입니다.
- 형식에 대한 메서드 호출 또는 정적 형식이 동적이 아닌 값에 대한 메서드 호출(§12.8.10.2)의 경우, 메서드 그룹 내에서 접근 가능한 메서드의 집합은 컴파일 타임에 이미 정해져 있습니다.
- 개체 생성 식(§12.8.17.2)의 경우 형식의 액세스 가능한 생성자 집합은 컴파일 타임에 알려져 있습니다.
- 인덱서 액세스(§12.8.12.4)의 경우 수신기의 액세스 가능한 인덱서 집합은 컴파일 타임에 알려져 있습니다.
이러한 경우, 알려진 함수 멤버 집합의 각 멤버에 대해 컴파일 시간 동안 제한된 검사를 수행하여, 런타임 중 절대로 호출되지 않을 것이 확실한지를 확인합니다. 각 함수 멤버 F에 대해 수정된 매개 변수와 인수 목록이 구성됩니다.
- 먼저
F제네릭 메서드이고 형식 인수가 제공된 경우 매개 변수 목록의 형식 매개 변수로 대체됩니다. 그러나 형식 인수가 제공되지 않은 경우 이러한 대체는 발생하지 않습니다. - 그런 다음 형식이 열려 있는 모든 매개 변수(즉, 형식 매개 변수 포함, §8.4.3 참조)는 해당 인수와 함께 제외됩니다.
F 검사를 통과하려면 다음 조건을 모두 충족해야 합니다.
-
F대한 수정된 매개 변수 목록은 §12.6.4.2측면에서 수정된 인수 목록에 적용할 수 있습니다. - 수정된 매개 변수 목록의 생성된 모든 형식은 해당 제약 조건을 충족합니다(§8.4.5).
- 위의 단계에서
F형식 매개 변수를 대체한 경우 해당 제약 조건이 충족됩니다. -
F이 정적 메서드라면, 메서드 그룹은 컴파일 타임에 수신기가 변수 또는 값으로 알려진 member_access에서 비롯된 것이 아니어야 합니다. -
F가 인스턴스 메서드인 경우, 메서드 그룹은 컴파일 타임에 형식으로 알려진 수신기에서 비롯된 member_access의 결과가 아니어야 합니다.
이 테스트를 통과한 후보가 없으면 컴파일 시간 오류가 발생합니다.
12.6.6 함수 멤버 호출
12.6.6.1 일반
이 하위 클래스는 런타임에 특정 함수 멤버를 호출하는 프로세스를 설명합니다. 바인딩 시간 프로세스가 이미 특정 멤버를 호출하도록 결정했으며, 아마도 후보 함수 멤버 집합에 오버로드 해석을 적용한 것으로 가정합니다.
호출 프로세스를 설명하기 위해 함수 멤버는 다음 두 가지 범주로 나뉩니다.
- 정적 함수 멤버 정적 메서드, 정적 속성 접근자 및 사용자 정의 연산자입니다. 정적 함수 멤버는 항상 가상이 아닌 멤버입니다.
- 인스턴스 함수 멤버 인스턴스 메서드, 인스턴스 생성자, 인스턴스 속성 접근자 및 인덱서 접근자입니다. 인스턴스 함수 멤버는 가상이 아니거나 가상이며 항상 특정 인스턴스에서 호출됩니다. 인스턴스는 인스턴스 식에 의해 계산되며 함수 멤버 내에서
this(§12.8.14)로 액세스할 수 있게 됩니다. 인스턴스 생성자에서는 인스턴스 식이 새로 할당된 객체를 나타냅니다.
함수 멤버 호출의 런타임 처리는 다음 단계로 구성됩니다. 여기서 M 함수 멤버이고 M 인스턴스 멤버인 경우 E 인스턴스 식입니다.
- 정적 함수 멤버인 경우
M:- 인수 목록은 §12.6.2설명된 대로 평가됩니다.
-
M호출됩니다.
- 그렇지 않은 경우 형식이 값 형식
E이고V다음에서M선언되거나 재정의V되는 경우:-
E평가됩니다. 이 평가에서 예외가 발생하면 추가 단계가 실행되지 않습니다. 인스턴스 생성자의 경우 이 평가는 새 개체에 대한 스토리지(일반적으로 실행 스택에서)를 할당하는 것으로 구성됩니다. 이 경우E변수로 분류됩니다. - 변수로 분류되지 않았거나 읽기 전용 구조체 형식(
E)이 아니고V읽기 전용 함수 멤버(§16.4.12)M가 아닌 경우E다음 중 하나입니다.- 입력 매개 변수(§15.6.2.3.2) 또는
-
readonly필드(§15.5.3) 또는 -
readonly참조 변수 또는 반환(§9.7)이면 's 형식의E임시 지역 변수가 만들어지고 해당 변수에E값이 할당됩니다. 그런 다음E해당 임시 지역 변수에 대한 참조로 다시 분류됩니다. 임시 변수는this내에서M으로 접근할 수 있지만, 다른 방법으로는 접근할 수 없습니다. 따라서E작성할 수 있는 경우에만 호출자가Mthis변경 내용을 관찰할 수 있습니다.
- 인수 목록은 §12.6.2설명된 대로 평가됩니다.
-
M호출됩니다.E에 의해 참조되는 변수가this에 의해 참조되는 변수가 됩니다.
-
- 그렇지 않은 경우:
-
E평가됩니다. 이 평가에서 예외가 발생하면 추가 단계가 실행되지 않습니다. - 인수 목록은 §12.6.2설명된 대로 평가됩니다.
-
E유형이 value_type인 경우, boxing 변환이 (§10.2.9) 수행되어E를 class_type으로 변환되도록 하고,E은 다음 단계에서 해당 class_type으로 여겨집니다. value_type가 enum_type인 경우, class_type는System.Enum;입니다. 그렇지 않으면,System.ValueType입니다. -
E값이 유효한지 확인합니다.E값이 null일 경우에는System.NullReferenceException이 발생하고, 추가 단계는 실행되지 않습니다. - 호출할 함수 멤버 구현은 다음과 같이 결정됩니다.
-
E의 바인딩 시간 형식이 인터페이스인 경우, 함수 멤버는M가 참조하는 인스턴스의 런타임 형식으로 제공되는E구현을 호출합니다. 이 함수 멤버는 인터페이스 매핑 규칙(§19.6.5)을 적용하여 참조M되는 인스턴스의E런타임 형식에서 제공하는 구현을 결정합니다. - 그렇지 않으면,
M이 가상 함수 멤버인 경우, 호출할 함수 멤버는M로 참조되는 인스턴스의 런타임 유형에서 제공하는E구현입니다. 이 함수 멤버는 이 참조하는 인스턴스의 런타임 형식에 따라M의 가장 파생된 구현을 결정하는 규칙(E)을 적용하여 결정됩니다. - 다른 경우,
M는 가상이 아닌 함수 멤버이며, 호출할 함수 멤버는 바로M입니다.
-
- 위의 단계에서 결정된 함수 멤버 구현이 호출됩니다.
E이 참조하는 개체가 이 개체가 참조하는 개체로 변경됩니다.
-
참고:§12.2 는 속성 액세스를 해당 함수 멤버(
get접근자 또는set접근자)를 호출하는 것으로 분류합니다. 위의 프로세스는 해당 접근자를 호출하기 위해 수행됩니다. 끝 메모
인스턴스 생성자(§12.8.17.2)의 호출 결과는 생성된 값입니다. 다른 함수 멤버의 호출 결과는 본문에서 반환된 값(있는 경우 )(§13.10.5)입니다.
12.6.6.2 박싱된 인스턴스에서 호출
value_type에 구현된 함수 멤버는 해당 value_type의 박싱된 인스턴스를 통해 다음과 같은 상황에서 호출할 수 있습니다.
- 함수 멤버가 class_type 형식에서 상속된 메서드를 재정의한 것이고, 그것이 해당 class_type인스턴스 표현식을 통해 호출되는 경우
참고: class_type은 항상
System.Object,System.ValueType또는System.Enum중 하나입니다. 끝 메모 - 함수 멤버가 인터페이스 함수 멤버의 구현이고 interface_type인스턴스 식을 통해 호출되는 경우.
- 함수 멤버가 대리자를 통해 호출되는 경우
이러한 상황에서 boxed 인스턴스는 value_type변수를 포함하는 것으로 간주되며, 이 변수는 함수 멤버 호출 내에서 이 변수가 참조하는 변수가 됩니다.
참고: 특히, 이는 boxed 인스턴스에서 함수 멤버가 호출될 때 함수 멤버가 boxed 인스턴스에 포함된 값을 수정할 수 있음을 의미합니다. 끝 메모
12.7 분해
분해는 식이 개별 식의 튜플로 변환되는 과정입니다. 분해는 간단한 할당의 대상이 튜플 식일 때, 해당 튜플의 각 요소에 할당할 값을 구하기 위해 사용됩니다.
E 식은 다음과 같이 n 요소가 있는 튜플 식으로 분해됩니다.
-
En요소가 있는 튜플 식인 경우 분해 결과는E식 자체입니다. - 그렇지 않으면,
E이(T1, ..., Tn)요소를 가진 튜플 형식n이 있는 경우,E은 임시 변수__v로 평가되고, 분해 결과는 표현식(__v.Item1, ..., __v.Itemn)입니다. - 그 외에,
E.Deconstruct(out var __v1, ..., out var __vn)식이 컴파일 타임에 고유한 인스턴스 또는 확장 메서드로 해결되면, 그 식이 계산되고, 분해의 결과는(__v1, ..., __vn)식입니다. 이러한 메서드를 디컨스트럭터라고 합니다. - 그렇지 않으면
E분해할 수 없습니다.
여기서 __v 및 __v1, ..., __vn 표시되지 않으며 액세스할 수 없는 임시 변수를 참조합니다.
참고:
dynamic형식의 식은 분해할 수 없습니다. 끝 메모
12.8 기본 표현
12.8.1 일반
기본 식에는 가장 간단한 형식의 식이 포함됩니다.
primary_expression
: literal
| interpolated_string_expression
| simple_name
| parenthesized_expression
| tuple_expression
| member_access
| null_conditional_member_access
| invocation_expression
| element_access
| null_conditional_element_access
| this_access
| base_access
| post_increment_expression
| post_decrement_expression
| null_forgiving_expression
| array_creation_expression
| object_creation_expression
| delegate_creation_expression
| anonymous_object_creation_expression
| typeof_expression
| sizeof_expression
| checked_expression
| unchecked_expression
| default_value_expression
| nameof_expression
| anonymous_method_expression
| pointer_member_access // unsafe code support
| pointer_element_access // unsafe code support
| stackalloc_expression
;
참고: 이 문법 규칙은 ANTLR이 처리하지 않는 상호 왼쪽 재귀 규칙 집합(
primary_expression,, ,member_accessinvocation_expression,element_accesspost_increment_expression,post_decrement_expressionnull_forgiving_expressionpointer_member_access및pointer_element_access)의 일부이므로 ANTLR을 사용할 수 없습니다. 표준 기술을 사용하여 문법을 변환하여 양방향 왼쪽 재귀를 제거할 수 있습니다. 이 표준은 모든 구문 분석 전략에 필요한 것은 아니며(예: LALR 파서가 필요하지 않음) 이렇게 하면 구조와 설명이 난독 처리되기 때문에 이 작업을 수행하지 않습니다. 끝 메모
pointer_member_access (§24.6.3) 및 pointer_element_access (§24.6.4)는 안전하지 않은 코드(§24)에서만 사용할 수 있습니다.
12.8.2 리터럴
기본 표현식은 리터럴(§6.4.5)로 구성되며 값으로 분류됩니다.
12.8.3 보간 문자열 표현식
interpolated_string_expression은 $, $@또는 @$중 하나 다음에 오는 " 문자 내의 텍스트로 구성됩니다. 따옴표 붙은 텍스트에는
보간된 문자열 식에는 두 가지 형태가 있습니다. 정규(interpolated_regular_string_expression) 및 축자(interpolated_verbatim_string_expression)입니다. 두 형식의 문자열 리터럴(§6.4.5.6)과 어휘적으로는 비슷하지만, 의미적으로는 다릅니다.
interpolated_string_expression
: interpolated_regular_string_expression
| interpolated_verbatim_string_expression
;
// interpolated regular string expressions
interpolated_regular_string_expression
: Interpolated_Regular_String_Start Interpolated_Regular_String_Mid?
('{' regular_interpolation '}' Interpolated_Regular_String_Mid?)*
Interpolated_Regular_String_End
;
regular_interpolation
: expression (',' interpolation_minimum_width)?
Regular_Interpolation_Format?
;
interpolation_minimum_width
: constant_expression
;
Interpolated_Regular_String_Start
: '$"'
;
// the following three lexical rules are context sensitive, see details below
Interpolated_Regular_String_Mid
: Interpolated_Regular_String_Element+
;
Regular_Interpolation_Format
: ':' Interpolated_Regular_String_Element+
;
Interpolated_Regular_String_End
: '"'
;
fragment Interpolated_Regular_String_Element
: Interpolated_Regular_String_Character
| Simple_Escape_Sequence
| Hexadecimal_Escape_Sequence
| Unicode_Escape_Sequence
| Open_Brace_Escape_Sequence
| Close_Brace_Escape_Sequence
;
fragment Interpolated_Regular_String_Character
// Any character except " (U+0022), \\ (U+005C),
// { (U+007B), } (U+007D), and New_Line_Character.
: ~["\\{}\u000D\u000A\u0085\u2028\u2029]
;
// interpolated verbatim string expressions
interpolated_verbatim_string_expression
: Interpolated_Verbatim_String_Start Interpolated_Verbatim_String_Mid?
('{' verbatim_interpolation '}' Interpolated_Verbatim_String_Mid?)*
Interpolated_Verbatim_String_End
;
verbatim_interpolation
: expression (',' interpolation_minimum_width)?
Verbatim_Interpolation_Format?
;
Interpolated_Verbatim_String_Start
: '$@"'
| '@$"'
;
// the following three lexical rules are context sensitive, see details below
Interpolated_Verbatim_String_Mid
: Interpolated_Verbatim_String_Element+
;
Verbatim_Interpolation_Format
: ':' Interpolated_Verbatim_String_Element+
;
Interpolated_Verbatim_String_End
: '"'
;
fragment Interpolated_Verbatim_String_Element
: Interpolated_Verbatim_String_Character
| Quote_Escape_Sequence
| Open_Brace_Escape_Sequence
| Close_Brace_Escape_Sequence
;
fragment Interpolated_Verbatim_String_Character
: ~["{}] // Any character except " (U+0022), { (U+007B) and } (U+007D)
;
// lexical fragments used by both regular and verbatim interpolated strings
fragment Open_Brace_Escape_Sequence
: '{{'
;
fragment Close_Brace_Escape_Sequence
: '}}'
;
위에서 정의한 어휘 규칙 6개는 다음과 같이 문맥에 민감한입니다.
| 규칙 | 상황별 요구 사항 |
|---|---|
| 보간된_정규_문자열_중 | Interpolated_Regular_String_Start후, 이후의 보간 사이 및 해당 Interpolated_Regular_String_End전에만 인식됩니다. |
| 보통_보간_형식 | 시작 콜론(:)이 어떤 종류의 대괄호(괄호/중괄호/정사각형) 내에 중첩되지 않은 경우 regular_interpolation 내에서만 인식됩니다. |
| Interpolated_Regular_String_End | Interpolated_Regular_String_Start 후에만 인식되며, 중간에 위치한 토큰이 Interpolated_Regular_String_Mid또는 regular_interpolation일부가 될 수 있는 토큰일 경우에만 인식됩니다. 여기에는 이러한 보간 내에 포함된 모든 interpolated_regular_string_expression토큰도 포함됩니다. |
| Interpolated_Verbatim_String_MidVerbatim_Interpolation_FormatInterpolated_Verbatim_String_End | 이 세 가지 규칙을 인식하면, 위에 언급된 각각의 일반 문법 규칙이 해당 축자 규칙으로 대체되는 것을 따릅니다. |
참고: 위의 규칙은 해당 정의가 언어의 다른 토큰과 겹치기 때문에 컨텍스트에 민감합니다. 끝 메모
참고: 위의 문법은 상황에 맞는 어휘 규칙으로 인해 ANTLR 준비가 되지 않았습니다. 다른 렉서 생성기와 마찬가지로 ANTLR은
어휘 모드를 사용하는 등 상황에 맞는 어휘 규칙을 지원하지만 구현 세부 정보이므로 이 사양의 일부가 아닙니다. 끝 메모
interpolated_string_expression는 값으로 분류됩니다. 암시적 보간 문자열 변환(System.IFormattable)을 사용하여 즉시 System.FormattableString 또는 형식으로 변환되는 경우, 보간된 문자열 표현식은 그 형식을 갖게 됩니다. 그렇지 않으면 형식이 string일 경우입니다.
참고: (
System.String) 및 (System.FormattableString)에 대한 문서에서 interpolated_string_expression의 가능한 형식 간 차이를 확인할 수 있습니다. 끝 메모
보간(string가 생성되어 interpolated_string_expression에 보간됩니다.
참고: 형식의 기본 형식이 결정되는 방법은
System.String(§C.2) 및System.FormattableString(§C.3)에 대한 설명서에 자세히 설명되어 있습니다. Regular_Interpolation_Format 및 Verbatim_Interpolation_Format와 동일한 표준 형식에 대한 설명은System.IFormattable설명서(§C.4)와 표준 라이브러리(§C)의 다른 형식에서 찾을 수 있습니다. 끝 메모
interpolation_minimum_width에서 constant_expression는 int로 암시적으로 변환되어야 합니다.
필드 너비는 이 constant_expression의 절대값이고, 맞춤은 이 constant_expression값의 부호(양수 또는 음수)입니다.
- 필드 너비 값이 서식이 지정된 문자열의 길이보다 작거나 같은 경우 서식이 지정된 문자열은 수정되지 않습니다.
- 그렇지 않으면 형식이 지정된 문자열의 길이가 필드 너비와 같도록 공백 문자로 채워집니다.
- 맞춤이 양수인 경우, 패딩을 앞에 추가하여 서식화된 문자열이 오른쪽에 맞춰집니다.
- 그렇지 않으면 패딩을 추가하여 왼쪽 맞춤됩니다.
위의 보간 서식 및 패딩을 포함하여 interpolated_string_expression전체적인 의미는 식을 메서드 호출로 변환하는 것으로 정의됩니다. 만약 식의 형식이 System.IFormattable 또는 System.FormattableString일 경우, 메서드는 System.Runtime.CompilerServices.FormattableStringFactory.Create(§C.3)를 사용하여 형식 System.FormattableString의 값을 반환합니다. 그렇지 않으면 형식은 string이 되고, 메서드는 string.Format(§C.2)을 사용하여 형식 string의 값을 반환합니다.
두 경우 모두 호출의 인수 목록은 각 삽입에 대한 형식 사양이 있는 형식 문자열 리터럴과 각 형식 사양에 해당하는 식에 대한 인수로 구성됩니다.
형식 문자열 리터럴은 다음과 같이 생성됩니다. 여기서 N은(는) interpolated_string_expression내의 보간 횟수를 나타냅니다. 형식 문자열 리터럴은 순서대로 구성됩니다.
- Interpolated_Regular_String_Start 문자 또는 Interpolated_Verbatim_String_Start 문자
- Interpolated_Regular_String_Mid 또는 Interpolated_Verbatim_String_Mid의 문자가 있는 경우
- 그런 다음
N ≥ 1부터I까지의 각 숫자에 대해0이N-1경우:- 자리 표시자 사양:
- 왼쪽 중괄호(
{) 문자 -
I10진수 표현 - 그런 다음 해당 regular_interpolation 또는 verbatim_interpolationinterpolation_minimum_width있는 경우 쉼표(
,)와 constant_expression 값의 10진수 표현이 뒤따릅니다. - Regular_Interpolation_Format 또는 Verbatim_Interpolation_Format문자(있는 경우), 해당 regular_interpolation 또는 verbatim_interpolation의 문자입니다.
- 오른쪽 중괄호(
}) 문자
- 왼쪽 중괄호(
- Interpolated_Regular_String_Mid 또는 Interpolated_Verbatim_String_Mid의 문자들은 해당 보간 바로 뒤에 위치합니다 (있는 경우)
- 자리 표시자 사양:
- 마지막으로 Interpolated_Regular_String_End 또는 Interpolated_Verbatim_String_End문자입니다.
후속 인수는 보간, 있을 경우 그에서 가져온 식들이며, 순서대로 입니다.
보간된 문자열 표현식 여러 보간을 포함할 때, 보간 표현식은 왼쪽에서 오른쪽 순으로 텍스트 순서에 따라 계산됩니다.
예제:
이 예제에서는 다음 형식 사양 기능을 사용합니다.
- 정수 형식을 대문자 16진수로 포맷하는
X형식 사양입니다. -
string값의 기본 형식은 값 자체입니다. - 지정된 최소 필드 너비 내에서 오른쪽 정렬되는 양수 정렬 값
- 지정된 최소 필드 너비 내에서 음수인 정렬 값으로 왼쪽 맞춤을 수행합니다.
- interpolation_minimum_width에 대한 정의된 상수 및
-
{{과}}은 각각{와}으로 형식이 지정됩니다.
주어진:
string text = "red";
int number = 14;
const int width = -4;
그러면:
| 보간된 문자열 식 |
string
와 같은 의미 |
값 |
|---|---|---|
$"{text}" |
string.Format("{0}", text) |
"red" |
$"{{text}}" |
string.Format("{{text}}) |
"{text}" |
$"{ text , 4 }" |
string.Format("{0,4}", text) |
" red" |
$"{ text , width }" |
string.Format("{0,-4}", text) |
"red " |
$"{number:X}" |
string.Format("{0:X}", number) |
"E" |
$"{text + '?'} {number % 3}" |
string.Format("{0} {1}", text + '?', number % 3) |
"red? 2" |
$"{text + $"[{number}]"}" |
string.Format("{0}", text + string.Format("[{0}]", number)) |
"red[14]" |
$"{(number==0?"Zero":"Non-zero")}" |
string.Format("{0}", (number==0?"Zero":"Non-zero")) |
"Non-zero" |
예제 종료
12.8.4 단순 이름
simple_name 는 식별자로 구성되며, 선택적으로 형식 인수 목록이 뒤따를 수 있습니다.
simple_name
: identifier type_argument_list?
;
simple_name 는 I 또는 I<A₁, ..., Aₑ>의 형식 중 하나이며, I 은 하나의 식별자이고 I<A₁, ..., Aₑ> 는 선택적 형식_인수_목록입니다.
type_argument_list 지정되지 않은 경우 e 0으로 간주합니다.
simple_name 다음과 같이 평가되고 분류됩니다.
-
e0이고 이름이 있는 지역 변수, 매개 변수 또는 상수를 직접 포함하는 지역 변수 선언 공간(§7.3) 내에I나타나는 경우 simple_name 해당 지역 변수, 매개 변수 또는 상수를 참조하고 변수 또는 값으로 분류됩니다. -
e이 0이고 simple_name이 제네릭 메서드 선언 내에는 있지만 method_declaration의 특성 외부에 위치하며, 그 선언에 이름이I인 형식 매개 변수가 포함되어 있을 경우, simple_name는 해당 형식 매개 변수를 참조합니다. - 그렇지 않으면 각 인스턴스 형식
T(§15.3.2)에 대해 즉시 바깥쪽 형식 선언의 인스턴스 형식으로 시작하고 각 바깥쪽 클래스 또는 구조체 선언의 인스턴스 형식(있는 경우)을 계속합니다.-
e0이고T선언에 이름I있는 형식 매개 변수가 포함된 경우 simple_name 해당 형식 매개 변수를 참조합니다. - 그렇지 않으면, 유형 인수를 사용한
I내의T에 대한 멤버 조회(e)가 일치를 생성하는 경우:-
T가 즉시 감싸고 있는 클래스 또는 구조체 형식의 인스턴스 형식이고, 조회 과정에서 하나 이상의 메서드를 식별하는 경우, 결과는this의 인스턴스 표현식과 관련된 메서드 그룹입니다. 형식 인수 목록을 지정한 경우 제네릭 메서드(§12.8.10.2)를 호출하는 데 사용됩니다. - 그렇지 않은 경우,
T이 즉시 바깥쪽 클래스 또는 구조체 유형의 인스턴스 유형이고, 조회에서 인스턴스 멤버가 식별되며, 인스턴스 생성자, 인스턴스 메서드 또는 인스턴스 접근자(§12.2.1)의 블록 내에서 참조가 발생할 때, 그 결과는 형식의 멤버 액세스(this.I)와 동일합니다. 이 문제는e0일 때만 발생할 수 있습니다. - 그렇지 않으면 결과는§12.8.7의 멤버 액세스 양식
T.I또는T.I<A₁, ..., Aₑ>과 동일합니다.
-
-
- 그렇지 않으면 각 네임스페이스
N에 대해, 단순_이름이 발생하는 네임스페이스부터 시작하여, 각 바깥쪽 네임스페이스(있는 경우라면)를 거쳐 전역 네임스페이스에 이르기까지, 엔터티가 위치할 때까지 다음 단계가 차례로 평가됩니다.-
e0이고IN네임스페이스의 이름인 경우 다음을 수행합니다.-
simple_name이 출현하는 위치가
N에 대한 네임스페이스 선언에 의해 감싸져 있고, 네임스페이스 선언에 이름 을 네임스페이스 또는 형식과 연결하는 extern_alias_directive 또는I이 포함된 경우, simple_name이 모호성이 발생하면 컴파일 시간 오류가 발생합니다. - 그렇지 않으면 simple_name는
I내의N라는 이름의 네임스페이스를 참조합니다.
-
simple_name이 출현하는 위치가
- 그렇지 않으면,
N에 이름I과e형식 매개 변수를 갖는 액세스 가능한 형식이 포함된 경우, 다음을 수행합니다.-
e이 0이고 simple_name이 발생하는 위치가N에 대한 네임스페이스 선언에 포함되어 있으며, 그 네임스페이스 선언이 이름 을 네임스페이스나 형식에 연결하는 extern_alias_directive 또는I을 포함하는 경우, simple_name은 모호하여 컴파일 시 오류가 발생합니다. - 그와 달리 namespace_or_type_name은/는 주어진 형식 인수로 구성된 형식을 참조합니다.
-
- 그렇지 않으면 simple_name이 발생하는 위치가
N의 네임스페이스 선언에 포함되는 경우:-
e이 0인 경우, 네임스페이스 선언에 extern_alias_directive 또는 using_alias_directive가 포함되어 있으며, 이 지시문이 이름I를 가져온 네임스페이스 또는 형식에 연결하는 경우, simple_name은 해당 네임스페이스 또는 형식을 참조합니다. - 그렇지 않은 경우 네임스페이스 선언의 using_namespace_directive가져온 네임스페이스에 이름
I및e형식 매개 변수가 있는 형식이 정확히 한 개 있는 경우 simple_name 지정된 형식 인수로 생성된 해당 형식을 참조합니다. - 그렇지 않으면 네임스페이스 선언의 using_namespace_directive가 가져온 네임스페이스에
I와e형식 매개변수를 가진 두 개 이상의 형식이 포함되어 있다면, simple_name이 모호해져 컴파일 시간 오류가 발생합니다.
-
참고: 이 전체 단계는 namespace_or_type_name(§7.8)를 처리하는 해당 단계와 정확히 유사합니다. 끝 메모
-
- 그렇지 않은 경우 0이고
e식별자I인 경우_simple_name 선언 식(§12.19)의 한 형태인 단순 무시입니다. - 그렇지 않으면 simple_name 정의되지 않고 컴파일 시간 오류가 발생합니다.
12.8.5 괄호가 있는 식
괄호로 묶인_표현 은 괄호로 묶인 식 으로 구성됩니다.
parenthesized_expression
: '(' expression ')'
;
괄호로 묶인 표현식은 괄호 안의 식을 평가하여 판단합니다. 괄호 안에
12.8.6 튜플 식
tuple_expression 튜플을 나타내며, 괄호로 묶인 뒤 쉼표로 구분되며 선택적으로 명명된 두 개 이상의 표현식으로 구성됩니다. deconstruction_expression은 암시적으로 형식화된 선언 식을 포함하는 튜플의 간결한 구문입니다.
tuple_expression
: '(' tuple_element (',' tuple_element)+ ')'
| deconstruction_expression
;
tuple_element
: (identifier ':')? expression
;
deconstruction_expression
: 'var' deconstruction_tuple
;
deconstruction_tuple
: '(' deconstruction_element (',' deconstruction_element)+ ')'
;
deconstruction_element
: deconstruction_tuple
| identifier
;
tuple_expression은(는) 튜플 유형으로 분류됩니다.
deconstruction_expressionvar (e1, ..., en)는 tuple_expression(var e1, ..., var en)를 간단히 표현한 것이며, 동일한 작동 방식을 따릅니다. 이는 deconstruction_expression안의 중첩된 deconstruction_tuple에 재귀적으로 적용됩니다.
따라서 deconstruction_expression 내에 중첩된 각 식별자는 선언 식(§12.19)을 도입합니다. 따라서 단순 할당의 왼쪽에서만 deconstruction_expression이 발생할 수 있습니다.
예: 다음 코드는 a, b 및 c의 세 가지 변수를 선언합니다. 각각은 정수이며 할당의 오른쪽에 있는 튜플에서 해당 값이 할당됩니다.
var (a, b, c) = (1, 2, 3); // a is 1, b is 2, and c is 3. var sum = a + b + c; // sum is 6.할당의 각각의 요소 자체는 분해 식일 수 있습니다. 예를 들어, 다음 분해 식은
a에서f까지의 6개의 변수를 할당합니다.var (a, b, (c, d, (e, f))) = (1, 2, (3, 4, (5, 6)));이 예제에서는 중첩된 튜플의 구조가 할당의 양쪽에서 일치해야 합니다.
왼쪽의 변수가 암시적으로 형식화되면 해당 식에 다음 형식이 있어야 합니다.
(int a, string? b) = (42, null); //OK var (c, d) = (42, null); // Invalid as type of d cannot be inferred (int e, var f) = (42, null); // Invalid as type of f cannot be inferred예제 종료
각 요소 식 Ei에 형식 Ti이 있을 때에만 튜플 식에 형식이 있습니다. 형식은 각 요소가 다음에 의해 정의되는 튜플 표현식의 요소 수와 동일한 튜플 유형이어야 합니다.
- 해당 위치의 튜플 요소에 이름이
Ni경우 튜플 형식 요소는Ti Ni합니다. - 다른 경우에는,
가 , , 형태일 때, 다음 중 어느 하나가하지 않는 한, 튜플 형식 요소는 , 가 되어야 합니다. - 튜플 식의 다른 요소에는 이름이
Ni, 또는 ... - 이름이 없는 다른 튜플 요소에는
Ni또는E.Ni또는E?.Ni형식의 튜플 요소 식이 있습니다. -
NiItemX형식입니다. 여기서X튜플 요소의 위치를 나타낼 수 있는0시작되지 않은 소수 자릿수의 시퀀스이며X요소의 위치를 나타내지 않습니다.
- 튜플 식의 다른 요소에는 이름이
- 그렇지 않으면 튜플 형식 요소는
Ti이어야 합니다.
튜플 식은 각 요소 식을 왼쪽에서 오른쪽으로 순서대로 평가하여 계산됩니다.
튜플 값은 튜플 식을 튜플 형식(§10.2.13)으로 변환하거나 값(§12.2.2)으로 재분류하거나 분해 할당(§12.23.2)의 대상으로 만들어 튜플 식에서 가져올 수 있습니다.
예제:
(int i, string) t1 = (i: 1, "One"); (long l, string) t2 = (l: 2, null); var t3 = (i: 3, "Three"); // (int i, string) var t4 = (i: 4, null); // Error: no type이 예제에서는 네 개의 튜플 식이 모두 유효합니다. 처음 두 항목인
t1과t2은 튜플 식의 형식을 사용하지 않고, 대신 암시적 튜플 변환을 적용합니다.t2의 경우, 암시적 튜플 변환은2에서long로, 그리고null에서string로의 암시적 변환에 의존합니다. 세 번째 튜플 식에는(int i, string)형식이 있으므로 해당 형식의 값으로 다시 분류할 수 있습니다. 반면에t4선언은 오류입니다. 두 번째 요소에 형식이 없기 때문에 튜플 식에는 형식이 없습니다.if ((x, y).Equals((1, 2))) { ... };이 예제에서는 튜플 식이 메서드 호출에 대한 유일한 인수인 경우 튜플이 때때로 여러 계층의 괄호로 이어질 수 있음을 보여 줍니다.
예제 종료
12.8.7 멤버 액세스
12.8.7.1 일반
member_access는 primary_expression, predefined_type, 또는 qualified_alias_member뒤에 "." 토큰이 오고, 그 뒤에 식별자, 선택적으로 type_argument_list가 이어지는 형태로 구성됩니다.
member_access
: primary_expression '.' identifier type_argument_list?
| predefined_type '.' identifier type_argument_list?
| qualified_alias_member '.' identifier type_argument_list?
;
predefined_type
: 'bool' | 'byte' | 'char' | 'decimal' | 'double' | 'float' | 'int'
| 'long' | 'object' | 'sbyte' | 'short' | 'string' | 'uint' | 'ulong'
| 'ushort'
;
qualified_alias_member 생산품은 §14.8정의되어 있습니다.
멤버_접근은 E.I 형식이나 E.I<A₁, ..., Aₑ>형식의 하나로, 여기서 E는 기본_표현식, 기_정의_타입 또는 한정_별칭_멤버이며,I은 단일 식별자이고 <A₁, ..., Aₑ>는 선택 사항인 타입_인자_목록입니다.
type_argument_list 지정되지 않은 경우 e 0으로 간주합니다.
형식의 primary_expression를 가진 dynamic은 동적으로 바인딩됩니다(§12.3.3). 이 경우 멤버 액세스는 형식 dynamic의 속성 액세스로 분류됩니다.
member_access의 의미를 결정하기 위한 아래 규칙들은 primary_expression의 컴파일 시간 형식 대신 런타임 형식을 사용하여 런타임 시 적용됩니다. 이 런타임 분류가 메서드 그룹으로 이어지는 경우, 멤버 액세스는 invocation_expression의 primary_expression이 되어야 합니다.
member_access 다음과 같이 평가되고 분류됩니다.
-
e이 0이고E이 네임스페이스이며E가I이라는 이름의 중첩된 네임스페이스인 경우, 그 결과는 해당 네임스페이스입니다. - 그렇지 않은 경우
E네임스페이스이고E이름I및K형식 매개 변수가 있는 액세스 가능한 형식을 포함하는 경우 결과는 지정된 형식 인수로 생성된 형식입니다. -
E이 형식으로 분류되고,E이 형식 매개 변수가 아니며, 형식 매개 변수를 사용한I의E멤버 조회(K)에서 일치가 발생하면,E.I이 다음과 같이 평가되고 분류됩니다.참고: 이러한 멤버 조회의 결과가 메서드 그룹이고
K0이면 메서드 그룹에 형식 매개 변수가 있는 메서드가 포함될 수 있습니다. 이렇게 하면 형식 인수 추론에 대해 이러한 메서드를 고려할 수 있습니다. 끝 메모-
I형식을 식별하는 경우 결과는 지정된 형식 인수를 사용하여 생성된 형식입니다. -
I하나 이상의 메서드를 식별하는 경우 결과는 연결된 인스턴스 식이 없는 메서드 그룹입니다. -
I정적 속성을 식별하는 경우 결과는 연결된 인스턴스 표현식이 없는 속성에 대한 접근입니다. -
I정적 필드를 식별하는 경우:- 필드가 읽기 전용이고 참조가 필드가 선언된 클래스 또는 구조체의 정적 생성자 외부에서 발생하는 경우 결과는 값, 즉
IE정적 필드의 값입니다. - 그렇지 않으면 결과는 변수이며, 구체적으로는
I의 정적 필드E입니다.
- 필드가 읽기 전용이고 참조가 필드가 선언된 클래스 또는 구조체의 정적 생성자 외부에서 발생하는 경우 결과는 값, 즉
-
I정적 이벤트를 식별하는 경우:- 이벤트가 선언된 클래스 또는 구조체 내에서 참조가 발생하고 이벤트가 event_accessor_declarations(§15.8.1) 없이 선언된 경우
E.II정적 필드인 것처럼 정확하게 처리됩니다. - 그렇지 않으면 연결된 인스턴스 식이 없는 이벤트 액세스 결과가 생성됩니다.
- 이벤트가 선언된 클래스 또는 구조체 내에서 참조가 발생하고 이벤트가 event_accessor_declarations(§15.8.1) 없이 선언된 경우
-
I상수가 식별되면 결과는 값, 즉 해당 상수의 값입니다. -
I열거형 멤버를 식별하는 경우 결과는 값, 즉 해당 열거형 멤버의 값입니다. - 그렇지 않으면
E.I잘못된 멤버 참조이며 컴파일 시간 오류가 발생합니다.
-
-
E이 속성 액세스, 인덱서 액세스, 변수 또는 값이며, 그 형식이T인 경우에, 에서I의 멤버 조회(T)가K형식 인수를 사용해 일치를 생성하면,E.I은 평가되어 다음과 같이 분류됩니다.- 먼저
E속성 또는 인덱서 액세스인 경우 속성 또는 인덱서 액세스의 값(§12.2.2)을 가져오고 E는 값으로 다시 분류됩니다. -
I이 하나 이상의 메서드를 식별하면 그 결과는E과 연관된 인스턴스 표현식이 있는 메서드 그룹이 됩니다. -
I이 인스턴스 속성을 식별할 경우, 결과는E에 연결된 인스턴스 식과 그 속성의 유형에 해당하는 연결된 유형의 속성 액세스가 됩니다.T이 클래스 형식인 경우,T에서 시작하여 그 기반 클래스를 검색하여 찾은 속성의 첫 번째 선언 또는 재정의에서 관련된 형식을 선택합니다. -
Tclass_type이고,I가 그 class_type의 인스턴스 필드를 식별하는 경우:-
E값이null경우System.NullReferenceExceptionthrow됩니다. - 그렇지 않으면, 필드가 읽기 전용이며 필드가 선언된 클래스의 인스턴스 생성자 외부에서 참조가 발생하는 경우, 결과는 특정 값입니다. 즉,
I이 참조하는 객체의E필드 값입니다. - 그렇지 않으면 결과는 변수, 즉
I이 참조하는 개체의 필드E입니다.
-
-
T가 struct_type이고I가 해당 struct_type의 인스턴스 필드를 식별하는 경우:-
E이 값이거나, 필드가 읽기 전용 상태이고 필드가 선언된 구조체의 인스턴스 생성자 외부에서 참조될 경우 그 결과는I에서 제공된 구조체 인스턴스의E필드의 값입니다. - 그렇지 않으면 결과는 변수이며, 이는
I에 주어진 구조체 인스턴스의 필드E입니다.
-
-
I인스턴스 이벤트를 식별하는 경우:- 이벤트가 선언된 클래스 또는 구조체 내에서 참조가 발생하고 이벤트가 event_accessor_declarations(§15.8.1) 없이 선언되고 참조가
a +=또는-=연산자의 왼쪽으로 발생하지 않는 경우E.I인스턴스 필드인 것처럼I정확하게 처리됩니다. - 그렇지 않으면 결과는
E에 연결된 인스턴스 식을 사용한 이벤트 액세스입니다.
- 이벤트가 선언된 클래스 또는 구조체 내에서 참조가 발생하고 이벤트가 event_accessor_declarations(§15.8.1) 없이 선언되고 참조가
- 먼저
- 그렇지 않으면
E.I을 확장 메서드 호출(§12.8.10.3)로 처리하려고 시도합니다. 실패하면E.I잘못된 멤버 참조이며 바인딩 시간 오류가 발생합니다.
12.8.7.2 동일한 단순 이름 및 형식 이름
양식 E.I에서 멤버 액세스 시, E이 단일 식별자이며, E (§12.8.4)으로서의 의 의미가 상수, 필드, 속성, 지역 변수 또는 매개 변수로서 E의 의미가 type_name (§7.8.1)과 동일한 유형인 경우, 이 두 가지 모두 가능한 E 의미가 허용됩니다.
E.I의 멤버 조회는 결코 모호하지 않으며, 이는 I이 두 경우 모두 반드시 E 유형의 멤버이기 때문입니다. 즉, 이 규칙은 그렇지 않으면 컴파일 시간 오류가 발생했을 E의 정적 멤버와 중첩된 형식에 대한 액세스를 허용합니다.
예제:
struct Color { public static readonly Color White = new Color(...); public static readonly Color Black = new Color(...); public Color Complement() => new Color(...); } class A { public «Color» Color; // Field Color of type Color void F() { Color = «Color».Black; // Refers to Color.Black static member Color = Color.Complement(); // Invokes Complement() on Color field } static void G() { «Color» c = «Color».White; // Refers to Color.White static member } }설명 목적으로만,
A클래스 내에서Color형식을 참조하는Color식별자의 발생은«...»으로 구분되며,Color필드를 참조하는 발생은 구분되지 않는 항목입니다.예제 종료
12.8.8 Null 조건부 멤버 액세스
null_conditional_member_access는 member_access(§12.8.7)의 조건부 버전이며, 결과 형식이 void인 경우 이는 바인딩 시간 오류입니다. 결과 형식이 void 수 있는 null 조건식의 경우 (§12.8.11)를 참조하세요.
null_conditional_member_accessprimary_expression 뒤에 두 개의 토큰 "?" 및 ".", 선택적 type_argument_list 있는 식별자, null_forgiving_operator 앞에 올 수 있는 0개 이상의 dependent_accesses로 구성됩니다.
null_conditional_member_access
: primary_expression '?' '.' identifier type_argument_list?
(null_forgiving_operator? dependent_access)*
;
dependent_access
: '.' identifier type_argument_list? // member access
| '[' argument_list ']' // element access
| '(' argument_list? ')' // invocation
;
null_conditional_projection_initializer
: primary_expression '?' '.' identifier type_argument_list?
;
null_conditional_member_accessE 식은 P?.A 형식입니다.
E 의미는 다음과 같이 결정됩니다.
P형식이 nullable 값 형식인 경우:T은P.Value.A유형입니다.T가 참조 형식도 아니고 널이 될 수 없는 값 형식도 아닌 형식 매개 변수인 경우에 컴파일 시간 오류가 발생합니다.T가 null이 될 수 없는 값 유형일 경우,E의 유형은T?이며,E의 의미는 다음과 같습니다.((object)P == null) ? (T?)null : P.Value.A단,
P한 번만 평가됩니다.그렇지 않으면
E의 유형은T이고,E의 의미는 다음의 의미와 같습니다.((object)P == null) ? (T)null : P.Value.A단,
P한 번만 평가됩니다.
그렇지 않으면:
표현식
T의 형식은P.A입니다.T가 참조 형식도 아니고 널이 될 수 없는 값 형식도 아닌 형식 매개 변수인 경우에 컴파일 시간 오류가 발생합니다.T가 null이 될 수 없는 값 유형일 경우,E의 유형은T?이며,E의 의미는 다음과 같습니다.((object)P == null) ? (T?)null : P.A단,
P한 번만 평가됩니다.그렇지 않으면
E의 유형은T이고,E의 의미는 다음의 의미와 같습니다.((object)P == null) ? (T)null : P.A단,
P한 번만 평가됩니다.
참고: 표현식 형태에서:
P?.A₀?.A₁만약
P가null로 평가되면,A₀와A₁는 평가되지 않습니다. 식이 null_conditional_member_access 연산 또는 null_conditional_element_access§12.8.13 연산의 시퀀스인 경우에도 동일하게 적용됩니다.끝 메모
null_conditional_projection_initializer는 null_conditional_member_access의 제한이며, 의미 체계가 동일합니다. 익명 개체 생성 식에서 프로젝션 이니셜라이저로만 발생합니다(§12.8.17.3).
12.8.9 Null-허용 표현
12.8.9.1 일반
null 허용식의 값, 형식, 분류(§12.2) 및 안전 컨텍스트(§16.4.15)는 기본 표현식의 값, 형식, 분류 및 안전 컨텍스트입니다.
null_forgiving_expression
: primary_expression null_forgiving_operator
;
null_forgiving_operator
: '!'
;
참고: 접미사 null 용서 연산자와 접두사 논리 부정 연산자(§12.9.4)는 동일한 어휘 토큰(!)으로 표현되지만, 각각 구별됩니다. 후자만 오버로드될 수 있으며(§15.10), null 용서 연산자의 정의가 수정됩니다.
끝 메모
null-forgiving 연산자를 동일한 식에 두 번 이상 적용하는 것은, 중간에 괄호가 있을지라도, 컴파일 시간 오류입니다.
예제: 다음은 모두 잘못되었습니다.
var p = q!!; // error: applying null_forgiving_operator more than once var s = ( ( m(t) ! ) )! // error: null_forgiving_operator applied twice to m(t)예제 종료
이 하위 클래스의 나머지 부분과 다음 형제 하위 클래스는 조건부로 표준화됩니다.
정적 null 상태 분석(§8.9.5)을 수행하는 컴파일러는 다음 사양을 준수해야 합니다.
null-forgiving 연산자는 컴파일러의 정적 null 상태 분석에 정보를 제공하는 컴파일 시간의 가상 연산입니다. 컴파일러가 식이 null일 수 있다고 판단하는 것을 무시하는 것과 null 허용 여부와 관련된 경고를 생성하는 컴파일러를 무시하는 두 가지 용도가 있습니다.
컴파일러의 정적 null 상태 분석에서 경고가 발생하지 않는 식에 null-forgiving 연산자를 적용하는 것은 오류가 아닙니다.
12.8.9.2 "null일 수도 있음" 결정 재정의
경우에 따라 컴파일러의 정적 null 상태 분석이 식의 null 상태를 maybe null인 것으로 결정하고, 다른 정보가 식이 null일 수 없음을 나타내는 경우에도 진단 경고를 발생시킬 수 있습니다. 이러한 식에 null-forgiving 연산자를 적용하면 이 식의 null 상태가 null이 아님을 컴파일러의 정적 null 상태 분석에 알립니다. 이는 진단 경고를 방지하고, 진행 중인 분석에 정보를 제공할 수도 있습니다.
예제: 다음을 고려합니다.
#nullable enable public static void M() { Person? p = Find("John"); // returns Person? if (IsValid(p)) { Console.WriteLine($"Found {p!.Name}"); // p can't be null } } public static bool IsValid(Person? person) => person != null && person.Name != null;
IsValid이(가)true을(를) 반환하면p을(를) 안전하게 역참조하여Name속성에 접근할 수 있으며,!을(를) 사용하여 "null 값의 역참조" 경고를 무시할 수 있습니다.예제 종료
예제: null 용서 연산자는 주의해서 사용해야 합니다. 다음을 고려하십시오.
#nullable enable int B(int? x) { int y = (int)x!; // quash warning, throw at runtime if x is null return y; }여기서 null-forgiving 연산자는 값 형식에 적용되어
x에 대한 경고를 무시합니다.x이 런타임에null인 경우,null를int로 캐스팅할 수 없기 때문에 예외가 발생합니다.예제 종료
12.8.9.3 다른 null 분석의 경고를 재정의
may be null 결정 사항을 위와 같이 재정의하는 것 외에도, 식에 하나 이상의 경고가 필요하다는 컴파일러의 정적 null 상태 분석 결정을 재정의할 필요가 있을 수 있는 다른 상황이 있을 수 있습니다. 이러한 식에 null-forgiving 연산자를 적용하면 컴파일러가 식에 대해 경고를 발생시키지 않도록 요청합니다. 이에 대한 응답으로 컴파일러는 경고를 발생시키지 않도록 선택할 수 있으며 추가 분석을 수정할 수도 있습니다.
예제: 다음을 고려합니다.
#nullable enable public static void Assign(out string? lv, string? rv) { lv = rv; } public string M(string? t) { string s; Assign(out s!, t ?? "«argument was null»"); return s; }
Assign메서드의 매개변수 유형, 즉lv&rv는string?이며,lv는 출력 매개변수로서 간단한 할당을 수행합니다.메서드
M은s유형의 변수string을Assign의 출력 매개 변수로 전달합니다. 컴파일러는s가 null일 수 없는 변수이기 때문에 경고를 출력합니다.Assign두 번째 인수가 null일 수 없다는 점을 감안할 때 null 용서 연산자는 경고를 분쇄하는 데 사용됩니다.예제 종료
조건부 표준 텍스트의 끝입니다.
12.8.10 호출 식
12.8.10.1 일반
invocation_expression은 메서드를 호출하는 데 사용되는 표현입니다.
invocation_expression
: primary_expression '(' argument_list? ')'
;
primary_expression는 delegate_type가 있는 경우에만 null_forgiving_expression일 수 있습니다.
다음 중 하나 이상의 조건이 충족되면 호출_표현식은 동적으로 바인딩됩니다(§12.3.3).
-
의 primary_expression은 컴파일 시간 형식이
dynamic입니다. - 선택적 인수 목록 중 적어도 하나 이상의 인수가 컴파일 시간 형식이
dynamic입니다.
이 경우 invocation_expression 형식 dynamic의 값으로 분류됩니다. 아래의 규칙은 invocation_expression의 의미를 결정하기 위해 런타임에 적용되며, 이때 primary_expression 및 컴파일 시간 형식이 dynamic인 인수의 컴파일 시간 형식 대신 런타임 형식을 사용합니다. 컴파일 시간 형식이 없는 경우, dynamic에서 메서드 호출은 §12.6.5에 설명된 대로 제한된 컴파일 시간 검사를 받게 됩니다.
primary_expression은 invocation_expression의 메서드 그룹이거나 delegate_type의 값이어야 합니다. 만약 primary_expression가 메서드 그룹이라면 invocation_expression은 메서드 호출(§12.8.10.2)입니다. primary_expression이 delegate_type값인 경우, invocation_expression는 대리자 호출(§12.8.10.4)입니다. primary_expression 이(가) 메서드 그룹이나 delegate_type값이 아닌 경우, 바인딩 시점 오류가 발생합니다.
선택적 argument_list(§12.6.2)는 메서드의 매개 변수에 대한 값 또는 변수 참조를 제공합니다.
invocation_expression 평가 결과는 다음과 같이 분류됩니다.
- invocation_expression 값이 반환되지 않는 메서드(§15.6.1) 또는 값이 반환되지 않는 대리자를 호출하면 결과는 없습니다. 아무것도로 분류되지 않은 식은 statement_expression (§13.7) 또는 lambda_expression 본문(§12.21)의 컨텍스트에서만 허용됩니다. 그렇지 않으면 바인딩 시간 오류가 발생합니다.
- 그렇지 않으면, invocation_expression이 참조 반환 메서드§15.6.1또는 참조 반환 대리자를 호출하는 경우, 결과는 메서드 또는 대리자의 반환 형식과 관련된 형식의 변수입니다. 호출이 인스턴스 메서드이며 수신기가
T클래스 형식인 경우,T에서 시작하여 해당 기본 클래스를 검색할 때 발견된 메서드의 첫 번째 선언 또는 재정의에서 연결된 형식이 선택됩니다. - 그렇지 않으면 호출_표현식은(는) 값반환 메서드(§15.6.1) 또는 값반환 델리게이트를 호출하며, 그 결과는 메서드 또는 델리게이트의 반환 유형과 관련된 유형의 값입니다. 호출이 인스턴스 메서드이며 수신기가
T클래스 형식인 경우,T에서 시작하여 해당 기본 클래스를 검색할 때 발견된 메서드의 첫 번째 선언 또는 재정의에서 연결된 형식이 선택됩니다.
12.8.10.2 메서드 호출
메서드 호출에서 invocation_expression의 primary_expression은 메서드 그룹이어야 합니다. 메서드 그룹은 호출할 메서드 하나 또는 호출할 특정 메서드를 선택할 오버로드된 메서드 집합을 식별합니다. 후자의 경우 호출할 특정 메서드의 결정은 argument_list인수 형식에서 제공하는 컨텍스트를 기반으로 합니다.
M(A) 메서드 그룹(아마도 M포함)이고 선택적 A을 포함하는 형태의 메서드 호출의 바인딩 시간 처리는 다음 단계로 구성됩니다.
- 메서드 호출에 대한 후보 메서드 집합이 생성됩니다. 메서드 그룹
F에 연결된 메서드M마다:-
F제네릭이 아닌 경우 다음과 같은 경우F후보입니다.-
M형식 인수 목록이 없으며 -
FA(§12.6.4.2)에 적용됩니다.
-
-
F제네릭이고M형식 인수 목록이 없는 경우 다음과 같은 경우F후보입니다. -
F제네릭이고M형식 인수 목록을 포함하는 경우 다음과 같은 경우F후보입니다.
-
- 후보 메서드 집합은 가장 파생된 형식의 메서드만 포함하도록 축소됩니다. 각 메서드
C.F에 대해, 이에 대응하는 선언된 형식C에서 메서드F가 선언된 경우,C의 기본 형식에 선언된 모든 메서드가 집합에서 제거됩니다. 또한Cobject이외의 클래스 형식인 경우 인터페이스 형식에 선언된 모든 메서드가 집합에서 제거됩니다.참고: 이 후자의 규칙은 메서드 그룹이
object이외의 유효 기본 클래스와 비어있지 않은 유효 인터페이스 집합이 있는 형식 매개 변수에 대한 멤버 조회의 결과인 경우에만 적용됩니다. 끝 메모 - 결과 후보 메서드 집합이 비어 있으면 다음 단계를 따라 추가 처리가 중단되고 대신 호출을 확장 메서드 호출로 처리하려고 시도합니다(§12.8.10.3). 이 작업이 실패하면 해당 메서드가 존재하지 않으며 바인딩 시간 오류가 발생합니다.
- 후보 메서드 집합의 가장 좋은 방법은 §12.6.4오버로드 확인 규칙을 사용하여 식별됩니다. 최상의 단일 메서드를 식별할 수 없는 경우 메서드 호출이 모호하며 바인딩 시간 오류가 발생합니다. 오버로드 해결을 수행할 때, 제네릭 메서드의 매개변수는 해당 메서드의 타입 매개변수에 대해 제공되거나 추론된 타입 인수로 대체된 후 고려한다.
위의 단계에서 바인딩 타임에 메서드를 선택하고 유효성을 검사하면 실제 런타임 호출은 §12.6.6설명된 함수 멤버 호출 규칙에 따라 처리됩니다.
참고: 위에서 설명한 해결 규칙의 직관적인 효과는 다음과 같습니다. 메서드 호출에서 호출하는 특정 메서드를 찾으려면 메서드 호출에서 나타내는 형식으로 시작하고 적용 가능한 액세스 가능하고 재정의되지 않는 메서드 선언이 하나 이상 발견될 때까지 상속 체인을 진행합니다. 그런 다음 해당 형식에 선언된 적용 가능하고 액세스 가능하며 재정의되지 않는 메서드 집합에 대해 형식 유추 및 오버로드 확인을 수행하고 선택한 메서드를 호출합니다. 메서드를 찾을 수 없는 경우 대신 호출을 확장 메서드 호출로 시도합니다. 끝 메모
12.8.10.3 Extension 메서드 호출
메서드 호출 중 하나의 양식에서(§12.6.6.2)
«expr» . «identifier» ( )
«expr» . «identifier» ( «args» )
«expr» . «identifier» < «typeargs» > ( )
«expr» . «identifier» < «typeargs» > ( «args» )
호출의 정상적인 처리에서 해당 메서드를 찾을 수 없으면 생성을 확장 메서드 호출로 처리하려고 시도합니다. «expr» 또는 «args»에 컴파일 시간 형식이 dynamic경우 확장 메서드가 적용되지 않습니다.
목표는 해당 정적 메서드 호출이 수행될 수 있도록 최상의 type_nameC찾는 것입니다.
C . «identifier» ( «expr» )
C . «identifier» ( «expr» , «args» )
C . «identifier» < «typeargs» > ( «expr» )
C . «identifier» < «typeargs» > ( «expr» , «args» )
다음과 같은 경우 확장 메서드 Cᵢ.Mₑ 를 사용할 수 있습니다 .
-
Cᵢ는 일반적이지 않고 중첩되지 않은 클래스입니다. -
Mₑ의 이름은 식별자입니다. -
Mₑ액세스 가능하며 위에서 설명한 대로 인수를 정적 메서드로 적용할 때 적용할 수 있습니다. - 암시적 식별, 참조 또는 박싱 변환이 표현식을
Mₑ의 첫 번째 매개 변수 형식으로 변환될 수 있습니다.
C 검색은 다음과 같이 진행됩니다.
- 가장 가까운 바깥쪽 네임스페이스 선언부터 시작하여 각각의 바깥쪽 네임스페이스 선언을 거쳐 최종적으로 포함하는 컴파일 단위에까지 이르는 순서로, 확장 메서드 후보군을 찾기 위한 연속적인 시도가 이루어집니다.
- 지정된 네임스페이스 또는 컴파일 단위가 적합한 확장 메서드
Cᵢ을 포함하는 제네릭이 아닌 형식 선언Mₑ을 직접 포함하는 경우, 해당 확장 메서드의 집합이 후보 집합이 됩니다. - 지정된 네임스페이스 또는 컴파일 단위에서 네임스페이스 지시문을 사용하여 가져온 네임스페이스가 제네릭이 아닌 형식 선언
Cᵢ과 관련 있는 적합한 확장 메서드Mₑ을 직접 포함하는 경우, 그 확장 메서드의 집합이 후보 집합입니다.
- 지정된 네임스페이스 또는 컴파일 단위가 적합한 확장 메서드
- 포함하는 네임스페이스 선언이나 컴파일 단위에서 후보 집합을 찾을 수 없으면 컴파일 시점 오류가 발생합니다.
- 그렇지 않으면 오버로드 해석은 §12.6.4에 설명된 대로 후보 집합에 적용됩니다. 최상의 방법을 하나도 찾지 못하면 컴파일 시간 오류가 발생합니다.
-
C최상의 메서드가 확장 메서드로 선언되는 형식입니다.
C 대상으로 사용하면 메서드 호출이 정적 메서드 호출(§12.6.6)으로 처리됩니다.
참고: 인스턴스 메서드 호출과 달리 expr이(가) null 참조로 평가될 때 예외가 발생하지 않습니다. 대신, 이
null값은 일반적인 정적 메서드 호출을 하듯이 확장 메서드에 전달됩니다. 이러한 호출에 응답하는 방법을 결정하는 것은 확장 메서드 구현에 달려 있습니다. 끝 메모
위의 규칙은 인스턴스 메서드가 확장 메서드보다 우선하고, 내부 네임스페이스 선언에서 사용할 수 있는 확장 메서드가 외부 네임스페이스 선언에서 사용할 수 있는 확장 메서드보다 우선하며, 네임스페이스에서 직접 선언된 확장 메서드가 using 네임스페이스 지시문을 사용하여 동일한 네임스페이스로 가져온 확장 메서드보다 우선한다는 것을 의미합니다.
예제:
public static class E { public static void F(this object obj, int i) { } public static void F(this object obj, string s) { } } class A { } class B { public void F(int i) { } } class C { public void F(object obj) { } } class X { static void Test(A a, B b, C c) { a.F(1); // E.F(object, int) a.F("hello"); // E.F(object, string) b.F(1); // B.F(int) b.F("hello"); // E.F(object, string) c.F(1); // C.F(object) c.F("hello"); // C.F(object) } }이 예제에서
B메서드는 첫 번째 확장 메서드보다 우선하며C메서드는 두 확장 메서드보다 우선합니다.public static class C { public static void F(this int i) => Console.WriteLine($"C.F({i})"); public static void G(this int i) => Console.WriteLine($"C.G({i})"); public static void H(this int i) => Console.WriteLine($"C.H({i})"); } namespace N1 { public static class D { public static void F(this int i) => Console.WriteLine($"D.F({i})"); public static void G(this int i) => Console.WriteLine($"D.G({i})"); } } namespace N2 { using N1; public static class E { public static void F(this int i) => Console.WriteLine($"E.F({i})"); } class Test { static void Main(string[] args) { 1.F(); 2.G(); 3.H(); } } }이 예제의 출력은 다음과 같습니다.
E.F(1) D.G(2) C.H(3)
D.G는 둘 다보다 우선C.G적으로 적용됩니다E.FD.F.C.F예제 종료
12.8.10.4 대리자 호출
invocation_expression의 primary_expression은 대리자 호출의 delegate_type값이어야 합니다. 또한 delegate_type를 delegate_type의 매개변수 목록과 동일한 매개변수 목록을 가진 함수 멤버로 간주할 경우, invocation_expression의 argument_list와 관련하여 delegate_type가 적용되어야 합니다 (§12.6.4.2).
-
D평가됩니다. 이 평가에서 예외가 발생하면 추가 단계가 실행되지 않습니다. - 인수 목록
A평가됩니다. 이 평가에서 예외가 발생하면 추가 단계가 실행되지 않습니다. -
D값이 유효한지 확인합니다.D값이null인 경우,System.NullReferenceException가 던져지고 추가 단계가 실행되지 않습니다. - 그렇지 않으면
D대리자 인스턴스에 대한 참조입니다. 함수 멤버 호출(§12.6.6)은 대리자 호출 목록의 호출 가능한 각 엔터티에서 수행됩니다. 인스턴스 및 인스턴스 메서드로 구성된 호출 가능한 엔터티의 경우 호출에 대한 인스턴스는 호출 가능한 엔터티에 포함된 인스턴스입니다.
매개 변수가 없는 여러 호출 목록에 대한 자세한 내용은 §21.6 을 참조하세요.
12.8.11 Null 조건부 호출 식
null_conditional_invocation_expression는 구문적으로 null_conditional_member_access(§12.8.8) 또는 null_conditional_element_access(§12.8.13) 중 하나이며, 여기서 최종 dependent_access는 호출 식(§12.8.10)입니다.
null_conditional_invocation_expression statement_expression(§13.7),anonymous_function_body(§12.21.1) 또는 method_body(§15.6.1)의 컨텍스트 내에서 발생합니다.
구문상 동등한 null_conditional_member_access 또는 null_conditional_element_access와 달리, null_conditional_invocation_expression 는 아무것으로도 분류될 수 있습니다.
null_conditional_invocation_expression
: null_conditional_member_access null_forgiving_operator? '(' argument_list? ')'
| null_conditional_element_access null_forgiving_operator? '(' argument_list? ')'
;
null_conditional_member_access 또는 null_conditional_element_access에 delegate_type이 있는 경우에만 선택적 null_forgiving_operator이 포함될 수 있습니다.
null_conditional_invocation_expression 표현식은 E 형식입니다. 여기서 P?A는 구문적으로 동등한 A 또는 null_conditional_element_access의 나머지이며, 따라서 가 A 또는 .로 시작하게 됩니다. 의 PA연결을 P 나타내도록 합니다A.
E이(가) 문장_표현식으로 발생하는 경우, E의 의미는 문장의의미와 같습니다.
if ((object)P != null) PA
단, P 한 번만 평가됩니다.
E이 익명 함수 본문 또는 메서드 본문로 사용될 때, E의 의미는 그것의 분류에 따라 달라집니다.
E아무것도로 분류되지 않으면 해당 의미는 블록의미와 같습니다.{ if ((object)P != null) PA; }단,
P한 번만 평가됩니다.그렇지 않으면
E의미는 블록의미와 같습니다.{ return E; }결과적으로, 이 블록의 의미는
E가 null_conditional_member_access(§12.8.8) 또는 null_conditional_element_access(§12.8.13)와 구문적으로 동일한지 여부에 따라 달라집니다.
12.8.12 요소 액세스
12.8.12.1 일반
element_access는 primary_expression 뒤에 "[" 토큰이 오고, 그 뒤에 argument_list가 오고, 마지막으로 "]" 토큰으로 구성됩니다.
argument_list 는 쉼표로 구분된 하나 이상의 인수로 구성되어 있습니다.
element_access
: primary_expression '[' argument_list ']'
;
element_access 및 pointer_element_access(§24.6.4) 대안을 모두 적용할 수 있는 경우 primary_expression 인식할 때 포함된 primary_expression 포인터 형식(§24.3)인 경우 후자를 선택해야 합니다.
element_access의 primary_expression은 array_initializer를 포함하지 않는 한 array_creation_expression이 될 수 없으며, stackalloc_initializer를 포함하지 않는 한 stackalloc_expression이 될 수 없습니다.
참고: 이 제한은 다음과 같은 잠재적으로 혼동될 수 있는 코드를 허용하지 않도록 존재합니다.
var a = new int[3][1];그렇지 않으면 다음과 같이 해석됩니다.
var a = (new int[3])[1];null_conditional_element_access(§12.8.13)에서도 비슷한 제한이 적용됩니다. 끝 메모
다음 중 하나 이상이 있는 경우 element_access 동적으로 바인딩됩니다(§12.3.3).
-
의 primary_expression은 컴파일 시간 형식이
dynamic입니다. -
argument_list 하나 이상의 식에 컴파일 시간 형식이 있습니다
dynamic.
이 경우 element_access 컴파일 시간 형식은 primary_expression 컴파일 시간 형식에 따라 달라집니다. 배열 형식이 있는 경우 컴파일 시간 형식은 해당 배열 형식의 요소 형식입니다. 그렇지 않으면 컴파일 시간 형식이 dynamic 고 element_access 형식dynamic의 값으로 분류됩니다. 런타임에 런타임 형식을 사용하여 element_access의 의미를 결정하기 위한 아래 규칙들이 적용됩니다. 이 규칙들은 컴파일 시간 형식이 인 primary_expression 및 dynamic 식의 컴파일 시간 형식 대신, 이들 식의 런타임 형식을 사용합니다.
primary_expression 컴파일 시간 형식dynamic이 없는 경우 요소 액세스는 §12.6.5에 설명된 대로 제한된 컴파일 시간 검사를 거칩니다.
예제:
var index = (dynamic)1; // index has compile-time type dynamic int[] a = {0, 1, 2}; var a_elem = a[index]; // dynamically bound, a_elem has compile-time type int string s = "012"; var s_elem = s[index]; // dynamcially bound, s_elem has compile-time type dynamic예제 종료
element_access primary_expression경우:
- 배열 형식의 값인 element_access 배열 액세스(§12.8.12.2);입니다.
- 형식의
string값으로, element_access 문자열 액세스(§12.8.12.3);입니다. - 그렇지 않으면 primary_expression 하나 이상의 인덱서 멤버가 있는 클래스, 구조체 또는 인터페이스 형식의 변수 또는 값이어야 합니다. 이 경우 element_access 인덱서 액세스(§12.8.12.4)입니다.
12.8.12.2 배열 액세스
배열 액세스의 경우 argument_list 명명된 인수 또는 참조 인수(§15.6.2.3)를 포함하지 않습니다.
argument_list 식 수는 array_type 순위와 같아야 하며 각 식은 다음과 같습니다.
- 형식
int,uint,long또는 ; 또는ulong - 단일 순위 배열 액세스 전용, 형식
Index또는 ; 또는Range - 은 위 형식 중 하나 이상으로 암시적으로 변환할 수 있습니다.
array_type primary_expression 인덱 P[A] 스 식의 P양식A 의 배열 액세스에 대한 런타임 처리는 다음 단계로 구성됩니다.
-
P평가됩니다. 이 평가에서 예외가 발생하면 추가 단계가 실행되지 않습니다. - 순서대로 argument_list 각 인덱스 식에 대해 왼쪽에서 오른쪽으로:
-
P값이 유효한지 확인합니다.P값이null인 경우,System.NullReferenceException가 던져지고 추가 단계가 실행되지 않습니다. - 이전 단계에서 형식
Range의 단일 인덱스 값을 생성한 경우 다음을 수행합니다.-
L을 참조하는 배열
P의 길이로 지정합니다. -
A는 L (§18.3)과 관련하여 유효한지 확인합니다. 그렇지 않으면 throwSystem.ArgumentOutOfRangeException되고 더 이상 단계가 실행되지 않습니다. -
L에 대한 시작 오프셋,
A및 항목 수 N은 (GetOffsetAndLength)에 대해 설명된 대로 결정됩니다. - 배열 액세스의 결과는 인덱스 S에서 시작하는
P요소 의 단순 복사본을 포함하는 배열입니다. N이 0이면 배열에 요소가 0이 됩니다.
-
L을 참조하는 배열
메모:S 와 N 은 모두 0일 수 있습니다($24.3). 빈 배열 인덱싱은 일반적으로 유효하지 않습니다. 그러나 빈 범위가 0부터 시작하는 인덱싱은 유효하며 빈 배열을 반환합니다. 또한 정의는 S 가 L, 과거 끝 인덱스(§18.1)가 되도록 허용합니다. 이 경우 N 은 0이 되고 빈 배열이 반환됩니다. 끝 메모
메모: 배열 액세스를 사용하여 배열의 요소 범위를 할당할 수 없습니다. 이는 인덱서 액세스(§12.8.12.4)와 다르며, 값으로
Range지정된 인덱스 범위에 대한 할당을 지원할 필요는 없습니다. 끝 메모
- 그렇지 않은 경우:
- 배열 액세스를 평가한 결과는 배열의 요소 형식에 대한 변수 참조(§9.5)입니다.
-
argument_list 각 식의 값은
P으로 참조되는 배열 인스턴스의 각 차원의 실제 경계에 대해 확인합니다. 하나 이상의 값이 범위를 벗어나면System.IndexOutOfRangeExceptionthrow되고 추가 단계가 실행되지 않습니다. - 인덱스 식에서 제공하는 배열 요소의 변수 참조가 계산되고 배열 액세스의 결과가 됩니다.
12.8.12.3 문자열 액세스
문자열 액세스의 경우 element_access argument_list 다음과 같은 단일 명명되지 않은 값 인수(§15.6.2.2)를 포함해야 합니다.
- 형식
int의Index또는Range - 암시적으로 위의 형식 중 하나 이상으로 변환할 수 있습니다.
형식의 primary_expression 단일 식인 폼 P[A]P 의 문자열 액세스에 대한 string 런타임 처리는 다음 단계로 구성됩니다.A
-
P평가됩니다. 이 평가에서 예외가 발생하면 추가 단계가 실행되지 않습니다. - 인덱스 식이 계산되고 결과 값의 형식이 T가 되도록 합니다.
- 그런 다음 이 값은
int에서 암시적 변환(Index)이 존재하는 형식 중 첫 번째 형식Range으로 변환됩니다. - 인덱스 식 또는 후속 암시적 변환을 계산하면 예외가 발생하는 경우 더 이상 인덱스 식이 평가되지 않으며 추가 단계가 실행되지 않습니다.
-
P값이 유효한지 확인합니다.P값이null인 경우,System.NullReferenceException가 던져지고 추가 단계가 실행되지 않습니다. - 이전 단계에서 형식
Range의 인덱스 값을 생성한 경우 다음을 수행합니다.- 문자열 액세스를 평가한 결과는 형식의
string값입니다. -
L을 참조하는 문자열
P의 길이로 지정합니다. -
A가 L (§18.3)과 관련하여 유효한지 확인합니다. 그렇지 않으면 throw되고 더 이상 단계가 실행되지 않습니다System.ArgumentOutOfRangeException. -
L에 대한 시작 오프셋,
A및 항목 수 N은 (GetOffsetAndLength)에 대해 설명된 대로 결정됩니다. - 문자열 액세스의 결과는 N이 0이면 문자열이 비어 있는 경우 S에서 시작하는
P문자를 복사하여 형성된 문자열입니다.
- 문자열 액세스를 평가한 결과는 형식의
메모:S 와 N 은 모두 0일 수 있습니다(§18.3). 빈 문자열 인덱싱은 일반적으로 유효하지 않습니다. 그러나 빈 범위가 0부터 시작하는 인덱싱은 유효하며 빈 문자열을 반환합니다. 또한 정의에서는 S 가 L, 마지막 끝 인덱스(§18.1)가 될 수 있습니다. 이 경우 N 은 0이 되고 빈 문자열이 반환됩니다. 끝 메모
- 그렇지 않은 경우:
- 문자열 액세스를 평가한 결과는 형식의
char값입니다. - 변환된 인덱스 식의 값은 .에서 참조하는 문자열 인스턴스의 실제 범위에 대해 검사됩니다
P. 값이 범위를 벗어나면 throwSystem.IndexOutOfRangeException되고 추가 단계가 실행되지 않습니다. - 문자열이 있는 변환된 인덱스 식의 오프셋에 있는 문자 값은 문자열
P액세스의 결과가 됩니다.
- 문자열 액세스를 평가한 결과는 형식의
12.8.12.4 인덱서 액세스
인덱서 액세스의 경우 element_access primary_expression 클래스, 구조체 또는 인터페이스 형식의 변수 또는 값이어야 하며, 이 형식은 element_access argument_list 관련하여 적용 가능한 하나 이상의 인덱서를 구현해야 합니다.
argument_list 포함하거나 out 인수를 포함하지 ref 않습니다.
클래스, 구조체 또는 인터페이스 형식P[A]의P과argument_list인T에 대한 양식을 가진 A 형태의 인덱서 액세스에 대한 바인딩 시간 처리는 다음 단계로 구성됩니다.
-
T가 제공하는 인덱서 세트가 생성됩니다. 집합에는T에 선언된 모든 인덱서 그리고T의 기본 형식으로 현재 컨텍스트(§7.5)에서 액세스할 수 있으며 재정의 선언이 아닌 인덱서가 포함됩니다. - 집합은 적용 가능하고 다른 인덱서에 의해 숨겨지지 않은 인덱서로 축소됩니다. 다음 규칙은 집합의 각 인덱서
S.I에 적용됩니다. 여기서S은 인덱서I가 선언된 형식입니다. - 결과 후보 인덱서 집합이 비어 있으면 해당 인덱서가 없고 바인딩 시간 오류가 발생합니다.
- 후보 인덱서 집합의 최상의 인덱서는 §12.6.4오버로드 확인 규칙을 사용하여 식별됩니다. 최상의 인덱서 하나만 식별할 수 없는 경우 인덱서 액세스가 모호하며 바인딩 시간 오류가 발생합니다.
- 최상의 인덱서의 접근자가 선택됩니다.
- 인덱서 액세스가 할당의 대상인 경우 인덱서에는 set 또는 ref get 접근자가 있어야 합니다. 그렇지 않으면 바인딩 시간 오류가 발생합니다.
- 그렇지 않으면 인덱서에 get 또는 ref get 접근자가 있어야 합니다. 그렇지 않으면 바인딩 시간 오류가 발생합니다.
인덱서 액세스의 런타임 처리는 다음 단계로 구성됩니다.
- 대상 primary_expression
P평가됩니다. -
argument_list
A인덱스 식은 왼쪽에서 오른쪽으로 순서대로 평가됩니다. - 바인딩 시간에 결정된 최상의 인덱서 사용:
12.8.13 Null 조건적 요소 접근
null_conditional_element_access는 primary_expression 뒤에 두 개의 토큰 "?" 및 "[", 다음으로 argument_list, "]" 토큰이 이어지며, 그 뒤에 0개 이상의 dependent_access가 따라올 수 있습니다. 이 중 일부는 null_forgiving_operator로 시작할 수 있습니다.
null_conditional_element_access
: primary_expression '?' '[' argument_list ']'
(null_forgiving_operator? dependent_access)*
;
null_conditional_element_access의 argument_list에는 out 또는 ref 인수가 포함되어서는 안 됩니다.
primary_expression이 array_initializer를 포함하지 않는 array_creation_expression 또는 stackalloc_initializer를 포함하지 않는 stackalloc_expression인 경우 null_conditional_element_access가 될 수 없습니다.
참고: 이 제한은 잠재적으로 혼동될 수 있는 코드를 허용하지 않도록 존재합니다. 제외된 항목의 예를 찾을 수 있는 element_access (§12.8.12)에서도 비슷한 제한이 적용됩니다. 끝 메모
null_conditional_element_access은 element_access(§12.8.12)의 조건부 버전이며, 결과 형식이 void이면 바인딩 시간 오류입니다. 결과 형식이 void 수 있는 null 조건식의 경우 (§12.8.11)를 참조하세요.
null_conditional_element_access 표현E은 P?[A]B; 형식입니다. 여기서 B는 있는 경우 dependent_accesses입니다.
E 의미는 다음과 같이 결정됩니다.
P형식이 nullable 값 형식인 경우:표현식
T의 형식은P.Value[A]B입니다.T가 참조 형식도 아니고 널이 될 수 없는 값 형식도 아닌 형식 매개 변수인 경우에 컴파일 시간 오류가 발생합니다.T가 null이 될 수 없는 값 유형일 경우,E의 유형은T?이며,E의 의미는 다음과 같습니다.((object)P == null) ? (T?)null : P.Value[A]B단,
P한 번만 평가됩니다.그렇지 않으면
E의 유형은T이고,E의 의미는 다음의 의미와 같습니다.((object)P == null) ? null : P.Value[A]B단,
P한 번만 평가됩니다.
그렇지 않으면:
표현식
T의 형식은P[A]B입니다.T가 참조 형식도 아니고 널이 될 수 없는 값 형식도 아닌 형식 매개 변수인 경우에 컴파일 시간 오류가 발생합니다.T가 null이 될 수 없는 값 유형일 경우,E의 유형은T?이며,E의 의미는 다음과 같습니다.((object)P == null) ? (T?)null : P[A]B단,
P한 번만 평가됩니다.그렇지 않으면
E의 유형은T이고,E의 의미는 다음의 의미와 같습니다.((object)P == null) ? null : P[A]B단,
P한 번만 평가됩니다.
참고: 표현식 형태에서:
P?[A₀]?[A₁]
P가null로 평가되면A₀나A₁는 평가되지 않습니다. 식이 null_conditional_element_access 또는 null_conditional_member_access§12.8.8 연산으로 구성된 시퀀스인 경우에도 동일합니다.끝 메모
12.8.14 이 액세스 권한
this_access은 this키워드로 구성됩니다.
this_access
: 'this'
;
this_access는 인스턴스 생성자, 인스턴스 메서드, 인스턴스 접근자(§12.2.1) 또는 종료자의 블록 내에서만 허용됩니다. 다음 의미 중 하나가 있습니다.
-
this이 클래스의 인스턴스 생성자 안에서 기본 표현로 사용되는 경우, 이는 값으로 분류됩니다. 값의 형식은 사용이 발생하는 클래스의 인스턴스 형식(§15.3.2)이며 이 값은 생성되는 개체에 대한 참조입니다. - 클래스의 인스턴스 메서드나 인스턴스 접근자 내에서
this가 주표현인 으로 사용될 때,this는 값으로 분류됩니다. 값의 형식은 사용이 발생하는 클래스의 인스턴스 형식(§15.3.2)이며, 값은 메서드 또는 접근자가 호출된 개체에 대한 참조입니다. -
this이(가) 구조체의 인스턴스 생성자에서 기본 표현로 사용될 때 변수로 분류됩니다. 변수의 형식은 사용이 발생하는 구조체의 인스턴스 형식(§15.3.2)이며, 변수는 생성되는 구조체를 나타냅니다.- 생성자 선언에 생성자 이니셜라이저가 없는 경우
this변수는 구조체 형식의 출력 매개 변수와 정확히 동일하게 동작합니다. 특히 이는 변수가 인스턴스 생성자의 모든 실행 경로에 확실히 할당되어야 하다는 것을 의미합니다. - 그렇지 않으면
this변수는 구조체 형식의ref매개 변수와 정확히 동일하게 동작합니다. 특히 이는 변수가 처음에 할당된 것으로 간주됨을 의미합니다.
- 생성자 선언에 생성자 이니셜라이저가 없는 경우
- 구조체의 인스턴스 메서드 또는 인스턴스 접근자 내에서
this가 으로 사용되는 경우, 이는 변수로 분류됩니다. 변수의 형식은 사용이 발생하는 구조체의 인스턴스 형식(§15.3.2)입니다.- 메서드 또는 접근자가 반복기(§15.15) 또는 비동기 함수(§15.14)
this가 아닌 경우 변수는 메서드 또는 접근자가 호출된 구조체를 나타냅니다.- 구조체가
readonly struct경우this변수는 구조체 형식의 입력 매개 변수와 정확히 동일하게 동작합니다. - 그렇지 않으면
this변수가 구조체 형식의ref매개 변수와 정확히 동일하게 동작합니다.
- 구조체가
- 메서드 또는 접근자가 반복기 또는 비동기 함수인 경우
this변수는 메서드 또는 접근자가 호출된 구조체의 복사 나타내며 구조체 형식의 값 매개 변수와 정확히 동일하게 동작합니다.
- 메서드 또는 접근자가 반복기(§15.15) 또는 비동기 함수(§15.14)
위에 나열된 컨텍스트 이외의 컨텍스트에서 this 사용하는 것은 컴파일 시간 오류입니다. 특히 this은 정적 메서드나 정적 속성 접근자, 또는 필드 선언의 변수 초기화자에서 참조할 수 없습니다.
12.8.15 기본 액세스
base_access는 키워드 base 뒤에 "." 토큰과 식별자, 선택적인 type_argument_list 또는 대괄호로 감싸진 argument_list로 구성됩니다.
base_access
: 'base' '.' identifier type_argument_list?
| 'base' '[' argument_list ']'
;
base_access는 현재 클래스나 구조체에서 비슷한 이름의 멤버에 의해 숨겨진 기본 클래스 멤버에 액세스하는 데 사용됩니다.
base_access는 인스턴스 생성자, 인스턴스 메서드, 인스턴스 접근자(§12.2.1) 또는 종결자의 본문에서만 허용됩니다. 클래스 또는 구조체에서 base.I 발생하면 I 해당 클래스 또는 구조체의 기본 클래스 멤버를 나타냅니다. 마찬가지로 클래스에서 base[E] 발생하면 해당 인덱서가 기본 클래스에 있어야 합니다.
바인딩 시 base_access 식의 base.I 및 base[E] 표현은 마치 ((B)this).I 및 ((B)this)[E]로 작성된 것처럼 정확하게 평가됩니다. 여기서 B은 해당 구문이 발생하는 클래스 또는 구조체의 기본 클래스입니다. 따라서 base.I와 base[E]은 this.I와 this[E]에 해당하며, this는 기본 클래스의 인스턴스로 간주되는 경우를 제외합니다.
base_access 가상 함수 멤버(메서드, 속성 또는 인덱서)를 참조하는 경우, 런타임에 호출할 함수 멤버를 결정하는 방식(§12.6.6)이 변경됩니다. 초기화되는 함수 멤버는 에 맞춰 함수 멤버의 가장 파생된 구현(B)을 찾아 결정됩니다. 이는 일반적으로 기본이 아닌 액세스에서처럼 this의 런타임 형식에 맞춘 것과는 다릅니다. 따라서 가상 함수 멤버의 재정의 내에서 base_access 사용하여 함수 멤버의 상속된 구현을 호출할 수 있습니다.
base_access 참조하는 함수 멤버가 추상인 경우 바인딩 시간 오류가 발생합니다.
참고:
this달리base자체로는 표현이 아닙니다. 키워드는 base_access 또는 constructor_initializer(§15.11.2)의 컨텍스트에서만 사용됩니다. 끝 메모
12.8.16 후위 증가 및 감소 연산자
post_increment_expression
: primary_expression '++'
;
post_decrement_expression
: primary_expression '--'
;
후위 증가 또는 감소 작업의 피연산자는 변수, 속성 액세스 또는 인덱서 액세스로 분류된 식이어야 합니다. 작업의 결과는 피연산자와 동일한 형식의 값입니다.
primary_expression 컴파일 시간 형식이 dynamic이면 연산자가 동적으로 바인딩되며(§12.3.3), post_increment_expression 또는 post_decrement_expression이 컴파일 시간 형식을 dynamic 가지며, 다음 규칙은 런타임에 primary_expression의 런타임 타입을 사용하여 적용됩니다.
후위 증가 또는 감소 작업의 피연산자가 속성 또는 인덱서 액세스인 경우, 그 속성 또는 인덱서에는 get 및 set 접근자가 모두 있어야 합니다. 그렇지 않으면 바인딩 시간 오류가 발생합니다.
단항 연산자 오버로드 확인(§12.4.4)이 적용되어 특정 연산자 구현을 선택합니다. 미리 정의된 ++ 및 -- 연산자는 sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal및 열거형 형식에 대해 존재합니다. 미리 정의된 ++ 연산자는 피연산자에서 1 추가하여 생성된 값을 반환하고, 미리 정의된 -- 연산자는 피연산자에서 1 빼서 생성된 값을 반환합니다. 확인된 컨텍스트에서 이 덧셈 또는 뺄셈의 결과가 결과 유형의 범위를 벗어나고, 결과 유형이 정수 형식 또는 열거형 형식일 경우 System.OverflowException가 던져집니다.
선택한 단항 연산자의 반환 형식에서 primary_expression형식으로 암시적 변환이 있어야 합니다. 그렇지 않으면 컴파일 시간 오류가 발생합니다.
폼 x++ 또는 x-- 후위 증가 또는 감소 연산의 런타임 처리는 다음 단계로 구성됩니다.
-
x변수로 분류되는 경우:-
x변수를 생성하도록 평가됩니다. -
x값이 저장됩니다. - 저장된
x값은 선택한 연산자의 피연산자 형식으로 변환되고 연산자는 이 값을 인수로 사용하여 호출됩니다. - 연산자가 반환한 값은
x형식으로 변환되고x이전 평가에서 제공한 위치에 저장됩니다. -
x에 저장된 값이 작업의 결과가 됩니다.
-
-
x속성 또는 인덱서 액세스 권한으로 분류되는 경우:- 인스턴스 식(
x이static이 아닌 경우) 및x에 연결된 인수 목록(x가 인덱서 액세스인 경우)이 평가되고, 그 결과는 후속 get 및 set 접근자 호출에서 사용됩니다. -
xget 접근자가 호출되고 반환된 값이 저장됩니다. - 저장된
x값은 선택한 연산자의 피연산자 형식으로 변환되고 연산자는 이 값을 인수로 사용하여 호출됩니다. - 연산자가 반환하는 값은
x형식으로 변환되고x집합 접근자는 값 인수로 이 값을 사용하여 호출됩니다. -
x에 저장된 값이 작업의 결과가 됩니다.
- 인스턴스 식(
및 ++ 연산자는 -- 접두사 표기법(§12.9.7)도 지원합니다. x 자체에는 작업 후 동일한 값이 있습니다.
연산자 ++ 또는 연산자 -- 구현은 후위 또는 전위 표기법을 사용하여 호출할 수 있습니다. 두 표기법은 별도의 연산자 구현을 사용할 수 없습니다.
12.8.17 새 연산자
12.8.17.1 일반
new 연산자는 형식의 새 인스턴스를 만드는 데 사용됩니다.
새로운 표현의 세 가지 형식이 있습니다.
- 개체 만들기 식은 클래스 형식 및 값 형식의 새 인스턴스를 만드는 데 사용됩니다.
- 배열 만들기 식은 배열 형식의 새 인스턴스를 만드는 데 사용됩니다.
- 대리자 생성 식은 대리자 형식의 인스턴스를 가져오는 데 사용됩니다.
new 연산자는 형식의 인스턴스를 만드는 것을 의미하지만 반드시 메모리 할당을 의미하지는 않습니다. 특히 값 형식의 인스턴스는 상주하는 변수 이외의 추가 메모리가 필요하지 않으며, 값 형식의 인스턴스를 만드는 데 new 사용될 때 할당이 발생하지 않습니다.
참고: 대리자 만들기 식이 항상 새 인스턴스를 만드는 것은 아닙니다. 식이 메서드 그룹 변환(§10.8) 또는 익명 함수 변환(§10.7)과 동일한 방식으로 처리되면 기존 대리자 인스턴스가 재사용될 수 있습니다. 끝 메모
12.8.17.2 개체 생성 식
12.8.17.2.1 일반
새 인스턴스를 만들기 위해 object_creation_expression이 class_type 또는 value_type를 생성하는 데 사용됩니다.
object_creation_expression
: 'new' type '(' argument_list? ')' object_or_collection_initializer?
| 'new' type object_or_collection_initializer
;
object_or_collection_initializer
: object_initializer
| collection_initializer
;
형식 은 object_creation_expression의 경우 class_type, value_type또는 type_parameter여야 합니다. 형식은 튜플 타입이거나 추상 또는 정적 클래스 타입이 될 수 없습니다.
선택적 argument_list(§12.6.2)는 형식class_type 또는 struct_type경우에만 허용됩니다.
개체 생성 식은 개체 이니셜라이저 또는 컬렉션 이니셜라이저를 포함하는 경우 생성자 인수 목록과 괄호를 묶는 것을 생략할 수 있습니다. 생성자 인수 목록을 생략하고 괄호를 묶는 것은 빈 인수 목록을 지정하는 것과 같습니다.
개체 이니셜라이저 또는 컬렉션 이니셜라이저를 포함하는 개체 만들기 식의 처리는 먼저 인스턴스 생성자를 처리한 다음 개체 이니셜라이저(§12.8.17.2.2) 또는 컬렉션 이니셜라이저(§12.8.17.2.3)에서 지정한 멤버 또는 요소 초기화를 처리하는 것으로 구성됩니다.
선택적 argument_list 인수 중 하나가 컴파일 시간 형식 dynamic인 경우, object_creation_expression은(는) 역동적으로 바인딩되며(§12.3.3), 컴파일 시간 형식이 인 dynamic 인수들의 런타임 형식에 따라 다음 규칙들이 런타임에 적용됩니다. 그러나 개체 생성은 §12.6.5설명된 대로 제한된 컴파일 시간 검사를 거칩니다.
가 new T(A)나 T일 때의 형태의 object_creation_expression의 바인딩 시간 처리는 A 선택적인 argument_list을 포함하여 다음 단계로 구성됩니다.
-
T이(가) value_type이고A이(가) 존재하지 않는 경우: - 그렇지 않으면
T가 타입 매개변수이고A이 없는 경우: - 그렇지 않으면
T가 class_type이거나 struct_type인 경우:-
T이(가) 추상 또는 정적 class_type인 경우, 컴파일 시 오류가 발생합니다. - 호출할 인스턴스 생성자는 §12.6.4오버로드 확인 규칙을 사용하여 결정됩니다. 후보 인스턴스 생성자 집합은
T선언된 모든 액세스 가능한 인스턴스 생성자로 구성되며A(§12.6.4.2)와 관련하여 적용할 수 있습니다. 후보 인스턴스 생성자 집합이 비어 있거나 최상의 단일 인스턴스 생성자를 식별할 수 없는 경우 바인딩 시간 오류가 발생합니다. -
object_creation_expression 결과는
T형식의 값, 즉 위의 단계에서 결정된 인스턴스 생성자를 호출하여 생성된 값입니다. - 그렇지 않으면 object_creation_expression 유효하지 않으며 바인딩 시간 오류가 발생합니다.
-
object_creation_expression 동적으로 바인딩되더라도 컴파일 시간 형식은 여전히 T.
새 형식의 T(A) 런타임 처리는 T가 class_type 또는 struct_type이고 A이 선택적 argument_list인 경우, 다음 단계로 구성됩니다.
-
T가 class_type이면 -
T가 구조체_유형인 경우:- 임시 지역 변수를 할당하여
T형식의 인스턴스를 만듭니다. struct_type 인스턴스 생성자는 생성되는 인스턴스의 각 필드에 값을 확실히 할당해야 하므로 임시 변수를 초기화할 필요가 없습니다. - 인스턴스 생성자는 함수 멤버 호출 규칙에 따라 호출됩니다(§12.6.6). 새로 할당된 인스턴스에 대한 참조는 인스턴스 생성자에 자동으로 전달되며 이 경우 해당 생성자 내에서 인스턴스에 액세스할 수 있습니다.
- 임시 지역 변수를 할당하여
12.8.17.2.2 개체 이니셜라이저
개체 이니셜라이저 개체의 0개 이상의 필드, 속성 또는 인덱싱된 요소에 대한 값을 지정합니다.
object_initializer
: '{' member_initializer_list? '}'
| '{' member_initializer_list ',' '}'
;
member_initializer_list
: member_initializer (',' member_initializer)*
;
member_initializer
: initializer_target '=' initializer_value
;
initializer_target
: identifier
| '[' argument_list ']'
;
initializer_value
: expression
| object_or_collection_initializer
;
개체 이니셜라이저는 { 및 } 토큰으로 묶고 쉼표로 구분된 멤버 이니셜라이저 시퀀스로 구성됩니다. 각 member_initializer 초기화 대상을 지정해야 합니다.
식별자는 초기화되는 개체의 액세스 가능한 필드 또는 속성의 이름을 지정해야 하며, 대괄호로 묶인 인수 목록은 초기화되는 개체의 액세스 가능한 인덱서에 대한 인수를 지정해야 합니다. 개체 이니셜라이저가 동일한 필드 또는 속성에 대해 둘 이상의 멤버 이니셜라이저를 포함하는 것은 오류입니다.
참고: 개체 이니셜라이저는 동일한 필드 또는 속성을 두 번 이상 설정할 수 없지만 인덱서에는 이러한 제한이 없습니다. 개체 이니셜라이저에는 인덱서를 참조하는 여러 이니셜라이저 대상이 포함될 수 있으며 동일한 인덱서 인수를 여러 번 사용할 수도 있습니다. 끝 메모
각 initializer_target 뒤에는 등호와 함께 식, 개체 이니셜라이저 또는 컬렉션 이니셜라이저가 있습니다. 개체 이니셜라이저 내의 식은 초기화 중인 새로 만든 개체를 참조할 수 없습니다.
initializer_target argument_list 형식 의 인수(Index§18.4.2) 또는 Range (§18.4.3)에 대한 암시적 지원은 없습니다.
등호 다음에 식을 지정하는 멤버 이니셜라이저는 대상에 대한 할당(§12.23.2)과 동일한 방식으로 처리됩니다.
등호 다음에 개체 이니셜라이저를 지정하는 멤버 이니셜라이저는 중첩된 개체 이니셜라이저( 즉, 포함된 개체의 초기화)입니다. 필드 또는 속성에 새 값을 할당하는 대신 중첩된 개체 이니셜라이저의 할당은 필드 또는 속성의 멤버에 대한 할당으로 처리됩니다. 중첩된 개체 이니셜라이저는 값 형식이 있는 속성이나 값 형식이 있는 읽기 전용 필드에 적용할 수 없습니다.
등호 다음에 컬렉션 이니셜라이저를 지정하는 멤버 이니셜라이저는 포함된 컬렉션의 초기화입니다. 대상 필드, 속성 또는 인덱서에 새 컬렉션을 할당하는 대신 이니셜라이저에 지정된 요소가 대상에서 참조하는 컬렉션에 추가됩니다. 대상은 §12.8.17.2.3지정된 요구 사항을 충족하는 컬렉션 형식이어야 합니다.
이니셜라이저 대상이 인덱서를 참조하는 경우 인덱서에 대한 인수는 항상 정확히 한 번 평가되어야 합니다. 설령 인수가 사용되지 않더라도 (예: 중첩된 빈 이니셜라이저로 인해), 그 자체로 부작용이 평가됩니다.
예제: 다음 클래스는 두 개의 좌표가 있는 점을 나타냅니다.
public class Point { public int X { get; set; } public int Y { get; set; } }다음과 같이
Point인스턴스를 만들고 초기화할 수 있습니다.Point a = new Point { X = 0, Y = 1 };이는 다음과 같은 효과가 있습니다.
Point __a = new Point(); __a.X = 0; __a.Y = 1; Point a = __a;여기서
__a보이지 않으며 액세스할 수 없는 임시 변수입니다.다음 클래스는 두 지점에서 만든 사각형과
Rectangle인스턴스의 생성 및 초기화를 보여 줍니다.public class Rectangle { public Point P1 { get; set; } public Point P2 { get; set; } }다음과 같이
Rectangle인스턴스를 만들고 초기화할 수 있습니다.Rectangle r = new Rectangle { P1 = new Point { X = 0, Y = 1 }, P2 = new Point { X = 2, Y = 3 } };이는 다음과 같은 효과가 있습니다.
Rectangle __r = new Rectangle(); Point __p1 = new Point(); __p1.X = 0; __p1.Y = 1; __r.P1 = __p1; Point __p2 = new Point(); __p2.X = 2; __p2.Y = 3; __r.P2 = __p2; Rectangle r = __r;여기서
__r,__p1및__p2표시되지 않으며 액세스할 수 없는 임시 변수입니다.
Rectangle의 생성자가 두 개의 포함된Point인스턴스를 할당하면, 이를 사용하여 새 인스턴스를 할당하는 대신 포함된Point인스턴스를 초기화할 수 있습니다.public class Rectangle { public Point P1 { get; } = new Point(); public Point P2 { get; } = new Point(); }다음 구문을 사용하여 새 인스턴스를 할당하는 대신 포함된
Point인스턴스를 초기화할 수 있습니다.Rectangle r = new Rectangle { P1 = { X = 0, Y = 1 }, P2 = { X = 2, Y = 3 } };이는 다음과 같은 효과가 있습니다.
Rectangle __r = new Rectangle(); __r.P1.X = 0; __r.P1.Y = 1; __r.P2.X = 2; __r.P2.Y = 3; Rectangle r = __r;예제 종료
12.8.17.2.3 컬렉션 이니셜라이저
컬렉션 이니셜라이저는 컬렉션의 요소를 지정합니다.
collection_initializer
: '{' element_initializer_list '}'
| '{' element_initializer_list ',' '}'
;
element_initializer_list
: element_initializer (',' element_initializer)*
;
element_initializer
: non_assignment_expression
| '{' expression_list '}'
;
expression_list
: expression (',' expression)*
;
컬렉션 이니셜라이저는 { 및 } 토큰으로 묶고 쉼표로 구분된 요소 이니셜라이저 시퀀스로 구성됩니다. 각 요소 이니셜라이저는 초기화되는 컬렉션 개체에 추가할 요소를 지정하며, { 및 } 토큰으로 묶고 쉼표로 구분된 식 목록으로 구성됩니다. 단일 표현식 요소 초기화는 중괄호 없이 작성할 수 있지만, 멤버 초기화와의 모호성을 피하기 위해 할당 표현식으로 사용할 수는 없습니다.
non_assignment_expression 생산은 §12.24에 정의되어 있습니다.
예제: 다음은 컬렉션 이니셜라이저를 포함하는 개체 만들기 식의 예입니다.
List<int> digits = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };예제 종료
컬렉션 이니셜라이저가 적용되는 컬렉션 객체는 System.Collections.IEnumerable을 구현하는 유형이어야 하며, 그렇지 않으면 컴파일 시간 오류가 발생합니다. 왼쪽에서 오른쪽으로 순서대로 지정된 각 요소에 대해 정규 멤버 조회가 적용되어 Add멤버를 찾습니다. 멤버 조회 결과가 메서드 그룹이 아니면 컴파일 시간 오류가 발생합니다. 그렇지 않으면 요소 이니셜라이저의 식 목록을 인수 목록으로 사용하여 오버로드 결정이 적용되고, 컬렉션 이니셜라이저는 결과 메서드를 호출합니다. 따라서 컬렉션 개체에는 각 요소 이니셜라이저에 대해 이름이 Add인 적용될 수 있는 인스턴스 또는 확장 메서드가 포함되어야 합니다.
예제: 다음은 이름과 전화 번호 목록이 있는 연락처를 나타내는 클래스와
List<Contact>만들기 및 초기화를 보여 줍니다.public class Contact { public string Name { get; set; } public List<string> PhoneNumbers { get; } = new List<string>(); } class A { static void M() { var contacts = new List<Contact> { new Contact { Name = "Chris Smith", PhoneNumbers = { "206-555-0101", "425-882-8080" } }, new Contact { Name = "Bob Harris", PhoneNumbers = { "650-555-0199" } } }; } }와 같은 효과가 있는
var __clist = new List<Contact>(); Contact __c1 = new Contact(); __c1.Name = "Chris Smith"; __c1.PhoneNumbers.Add("206-555-0101"); __c1.PhoneNumbers.Add("425-882-8080"); __clist.Add(__c1); Contact __c2 = new Contact(); __c2.Name = "Bob Harris"; __c2.PhoneNumbers.Add("650-555-0199"); __clist.Add(__c2); var contacts = __clist;여기서
__clist,__c1및__c2표시되지 않으며 액세스할 수 없는 임시 변수입니다.예제 종료
12.8.17.3 익명 개체 생성 식
anonymous_object_creation_expression 익명 형식의 객체 생성을 위한 표현식입니다.
anonymous_object_creation_expression
: 'new' anonymous_object_initializer
;
anonymous_object_initializer
: '{' member_declarator_list? '}'
| '{' member_declarator_list ',' '}'
;
member_declarator_list
: member_declarator (',' member_declarator)*
;
member_declarator
: simple_name
| member_access
| null_conditional_projection_initializer
| base_access
| identifier '=' expression
;
익명 개체 이니셜라이저는 익명 형식을 선언하고 해당 형식의 인스턴스를 반환합니다. 익명 형식은 object을(를) 직접 상속하는 이름 없는 클래스 유형입니다. 익명 형식의 멤버는 형식의 인스턴스를 만드는 데 사용되는 익명 개체 이니셜라이저에서 유추된 읽기 전용 속성의 시퀀스입니다. 특히, 폼의 익명 개체 이니셜라이저
new {
p₁=e₁,p₂=e₂, ...
pv=ev}
는 양식의 익명 형식을 선언합니다.
class __Anonymous1
{
private readonly «T1» «f1»;
private readonly «T2» «f2»;
...
private readonly «Tn» «fn»;
public __Anonymous1(«T1» «a1», «T2» «a2»,..., «Tn» «an»)
{
«f1» = «a1»;
«f2» = «a2»;
...
«fn» = «an»;
}
public «T1» «p1» { get { return «f1»; } }
public «T2» «p2» { get { return «f2»; } }
...
public «Tn» «pn» { get { return «fn»; } }
public override bool Equals(object __o) { ... }
public override int GetHashCode() { ... }
}
여기서 각 «Tx»는 해당 식 «ex»의 형식입니다.
member_declarator 사용되는 식에는 형식이 있어야 합니다. 따라서 member_declarator 식이 null 또는 익명 함수가 되는 것은 컴파일 시간 오류입니다.
익명 형식의 이름과 해당 Equals 메서드에 대한 매개 변수의 이름은 컴파일러에서 자동으로 생성되며 프로그램 텍스트에서 참조할 수 없습니다.
동일한 프로그램 내에서 동일한 이름의 속성 시퀀스를 지정하고 동일한 순서로 컴파일 시간 형식을 지정하는 두 개의 익명 개체 이니셜라이저는 동일한 익명 형식의 인스턴스를 생성합니다.
예제: 예제에서
var p1 = new { Name = "Lawnmower", Price = 495.00 }; var p2 = new { Name = "Shovel", Price = 26.95 }; p1 = p2;
p1및p2동일한 익명 형식이므로 마지막 줄의 할당이 허용됩니다.예제 종료
익명 형식의 Equals 및 GetHashcode 메서드는 상속된 object메서드를 재정의하며, 속성의 Equals 및 GetHashcode로 정의됩니다. 따라서 동일한 익명 형식의 두 인스턴스는 모든 속성이 같을 때에만 동일합니다.
멤버 선언자는 간단한 이름(§12.8.4), 멤버 액세스(§12.8.7), null 조건부 프로젝션 이니셜라이저 §12.8.8 또는 기본 액세스(§12.8.15)로 축약할 수 있습니다. 이를 프로젝션 이니셜라이저라고 하며, 이는 동일한 이름의 속성에 대한 선언 및 할당의 간단한 표현입니다. 특히 여러 양식의 멤버 선언자
«identifier», «expr» . «identifier» 및 «expr» ? . «identifier»
각각 다음의 것과 정확히 동일합니다.
«identifier» = «identifier», «identifier» = «expr» . «identifier» 및 «identifier» = «expr» ? . «identifier»
따라서 프로젝션 이니셜라이저에서 식별자는 값과 값이 할당된 필드 또는 속성을 모두 선택합니다. 직관적으로 프로젝션 이니셜라이저는 값뿐만 아니라 값 이름도 프로젝션합니다.
12.8.17.4 배열 만들기 식
새 인스턴스를 만들기 위해 array_creation_expression이(가) array_type에 사용됩니다.
array_creation_expression
: 'new' non_array_type '[' expression_list ']' rank_specifier*
array_initializer?
| 'new' array_type array_initializer
| 'new' rank_specifier array_initializer
;
첫 번째 폼의 배열 생성 식은 식 목록에서 각 개별 식을 삭제한 결과 형식의 배열 인스턴스를 할당합니다.
예제: 배열 생성 식은
new int[10,20]int[,]형식의 배열 인스턴스를 생성하고, 새int[10][,]배열 생성 식은int[][,]형식의 배열 인스턴스를 생성합니다. 예제 종료
식 목록의 각 식은 int, uint, long또는 ulong형식이거나 이러한 형식 중 하나 이상으로 암시적으로 변환할 수 있어야 합니다. 각 식의 값은 새로 할당된 배열 인스턴스에서 해당 차원의 길이를 결정합니다. 배열 차원의 길이는 음수가 아니어야 하므로 식 목록에 음수 값이 있는 상수 식이 있는 것은 컴파일 시간 오류입니다.
안전하지 않은 컨텍스트(§24.2)를 제외하고 배열의 레이아웃은 지정되지 않습니다.
첫 번째 폼의 배열 생성 식에 배열 이니셜라이저가 포함된 경우 식 목록의 각 식은 상수여야 하며 식 목록에서 지정한 순위 및 차원 길이는 배열 이니셜라이저의 식과 일치해야 합니다.
두 번째 또는 세 번째 폼의 배열 생성 식에서 지정된 배열 형식 또는 순위 지정자의 순위는 배열 이니셜라이저의 순위와 일치해야 합니다. 개별 차원 길이는 배열 이니셜라이저의 해당 중첩 수준 각각에 있는 요소 수에서 유추됩니다. 따라서 다음 선언의 초기화자 식
var a = new int[,] {{0, 1}, {2, 3}, {4, 5}};
정확히 다음에 해당합니다.
var a = new int[3, 2] {{0, 1}, {2, 3}, {4, 5}};
세 번째 형태의 배열 생성 식은 유형이 암시된 배열 생성 식 이라고 한다. 배열의 요소 형식이 명시적으로 지정되지 않았지만 배열 이니셜라이저에 있는 식 집합의 가장 일반적인 형식(§12.6.3.16)으로 결정된다는 점을 제외하면 두 번째 양식과 비슷합니다. 다차원 배열의 경우, 즉 rank_specifier에 하나 이상의 쉼표가 포함된 배열에서는 이 집합이 중첩된 array_initializer에서 발견된 모든 식으로 구성됩니다.
배열 초기화자는 §17.7에 더 자세히 설명되어 있습니다.
배열 생성 식을 계산한 결과는 값, 즉 새로 할당된 배열 인스턴스에 대한 참조로 분류됩니다. 배열 만들기 식의 런타임 처리는 다음 단계로 구성됩니다.
-
식 목록의 차원 길이 식은 왼쪽에서 오른쪽으로 순서대로 평가됩니다. 각 식을 평가한 후 암시적 변환(§10.2)을 다음 형식 중 하나로 변환합니다.
int,uint,long,ulong. 암시적 변환이 존재하는 이 목록의 첫 번째 형식이 선택됩니다. 식의 평가 또는 후속 암시적 변환으로 인해 예외가 발생하는 경우 더 이상 식이 평가되지 않으며 추가 단계가 실행되지 않습니다. - 차원 길이에 대한 계산 값은 다음과 같이 유효성이 검사됩니다. 하나 이상의 값이 0보다 작으면
System.OverflowExceptionthrow되고 추가 단계가 실행되지 않습니다. - 지정된 차원 길이를 가진 배열 인스턴스가 할당됩니다. 새 인스턴스를 할당하는 데 사용할 수 있는 메모리가 충분하지 않으면
System.OutOfMemoryExceptionthrow되고 추가 단계가 실행되지 않습니다. - 새 배열 인스턴스의 모든 요소는 기본값(§9.3)으로 초기화됩니다.
- 배열 만들기 식에 배열 이니셜라이저가 포함된 경우 배열 이니셜라이저의 각 식이 평가되고 해당 배열 요소에 할당됩니다. 계산 및 할당은 배열 초기화에서 식이 작성된 순서대로 수행됩니다. 즉, 요소는 인덱스가 증가하는 순서로 초기화되고, 가장 안쪽 차원이 먼저 증가합니다. 지정된 식 또는 해당 배열 요소에 대한 후속 할당을 평가하면 예외가 발생하면 더 이상 요소가 초기화되지 않으며 나머지 요소에는 해당 기본값이 포함됩니다.
배열 생성 식은 배열 형식의 요소를 사용하여 배열을 인스턴스화할 수 있지만 이러한 배열의 요소는 수동으로 초기화해야 합니다.
예제: 문장
int[][] a = new int[100][];는
int[]형식의 100 요소를 사용하여 1차원 배열을 만듭니다. 각 요소의 초기 값은null. 동일한 배열 만들기 식이 하위 배열 및 문을 인스턴스화할 수도 없습니다.int[][] a = new int[100][5]; // Error컴파일 시간 오류가 발생합니다. 하위 배열의 인스턴스화는 다음과 같이 수동으로 수행할 수 있습니다.
int[][] a = new int[100][]; for (int i = 0; i < 100; i++) { a[i] = new int[5]; }예제 종료
참고: 배열 배열에 "사각형" 셰이프가 있는 경우, 즉 하위 배열의 길이가 모두 같은 경우 다차원 배열을 사용하는 것이 더 효율적입니다. 위의 예제에서 배열 배열의 인스턴스화는 101개의 개체(하나의 외부 배열 및 100개의 하위 배열)를 만듭니다. 반면,
int[,] a = new int[100, 5];는 단일 개체, 2차원 배열만 만들고 단일 문에서 할당을 수행합니다.
끝 메모
예제: 다음은 암시적으로 형식화된 배열 생성 식의 예입니다.
var a = new[] { 1, 10, 100, 1000 }; // int[] var b = new[] { 1, 1.5, 2, 2.5 }; // double[] var c = new[,] { { "hello", null }, { "world", "!" } }; // string[,] var d = new[] { 1, "one", 2, "two" }; // Error마지막 식은
int또는string모두 암시적으로 다른 형식으로 변환할 수 없으므로 컴파일 시간 오류가 발생하므로 가장 일반적인 형식이 없습니다. 이 경우 명시적으로 형식화된 배열 생성 식을 사용해야 합니다(예:object[]형식 지정). 또는 요소 중 하나를 공통 기본 형식으로 변환하여, 그것이 유추된 요소 형식이 될 수 있습니다.예제 종료
암시적으로 형식화된 배열 생성 식을 익명 개체 이니셜라이저(§12.8.17.3)와 결합하여 익명 형식의 데이터 구조를 만들 수 있습니다.
예제:
var contacts = new[] { new { Name = "Chris Smith", PhoneNumbers = new[] { "206-555-0101", "425-882-8080" } }, new { Name = "Bob Harris", PhoneNumbers = new[] { "650-555-0199" } } };예제 종료
12.8.17.5 대리자 만들기 식
delegate_creation_expression은 delegate_type인스턴스를 얻기 위해 사용됩니다.
delegate_creation_expression
: 'new' delegate_type '(' expression ')'
;
대리자 생성 식의 인수는 메서드 그룹, 익명 함수 또는 컴파일 시간 형식 dynamic이나 위임 형식중 하나의 타입이어야 합니다. 인수가 메서드 그룹인 경우 메서드를 식별하고 인스턴스 메서드의 경우 대리자를 만들 개체를 식별합니다. 인수가 무명 함수인 경우 대리자 대상의 매개 변수 및 메서드 본문을 직접 정의합니다. 인수가 값이면 복사본을 만들 대리자 인스턴스를 식별합니다.
식 컴파일 시간 형식이 dynamic경우 delegate_creation_expression 동적으로 바인딩됩니다(§12.8.17.5). 아래 규칙은 런타임에 식런타임에 적용됩니다. 그렇지 않으면 컴파일 시간에 규칙이 적용됩니다.
delegate_creation_expression의 바인딩 시간 처리는 'new D(E)' 형식의 D인 와 E인 을 포함하는 다음 단계로 구성됩니다.
E이(가) 메서드 그룹인 경우, 대리자 생성 식은 에서E로의 메서드 그룹 변환(D)과 동일한 방식으로 처리됩니다.값인 경우
E(E)와 호환되어야 하며, 결과는 호출되는 단일 항목 호출 목록을 사용하여 새로 만든 대리자에D대한 참조입니다.E
런타임 처리는 새
- 만약
E이 메서드 그룹이라면, 대리자 생성 식은 에서E로의 메서드 그룹 변환(D)으로 평가됩니다. -
E익명 함수인 경우 대리자 생성은ED(§10.7)로 익명 함수 변환으로 평가됩니다. -
E가 delegate_type의 값인 일 경우:-
E평가됩니다. 이 평가에서 예외가 발생하면 추가 단계가 실행되지 않습니다. -
E값이null인 경우,System.NullReferenceException가 던져지고 추가 단계가 실행되지 않습니다. - 대리자 형식
D새 인스턴스가 할당됩니다. 새 인스턴스를 할당하는 데 사용할 수 있는 메모리가 충분하지 않으면System.OutOfMemoryExceptionthrow되고 추가 단계가 실행되지 않습니다. - 새 대리자 인스턴스 객체는
E을(를) 호출하는 단일 진입점 호출 목록으로 초기화됩니다.
-
대리자의 호출 목록은 대리자가 인스턴스화될 때 결정된 다음 대리자의 전체 수명 동안 일정하게 유지됩니다. 즉, 대리자가 생성된 후에는 그 대상이 되는 호출 가능한 엔터티를 변경할 수 없습니다.
참고: 두 대리자가 결합되거나 다른 대리자가 제거되면 새 대리자가 생성됩니다. 기존 대리자의 콘텐츠는 변경되지 않습니다. 끝 메모
속성, 인덱서, 사용자 정의 연산자, 인스턴스 생성자, 종료자 또는 정적 생성자를 참조하는 대리자를 만들 수 없습니다.
예제: 위에서 설명한 대로 메서드 그룹에서 대리자를 만들 때 대리자의 매개 변수 목록 및 반환 형식에 따라 선택할 오버로드된 메서드가 결정됩니다. 예제에서
delegate double DoubleFunc(double x); class A { DoubleFunc f = new DoubleFunc(Square); static float Square(float x) => x * x; static double Square(double x) => x * x; }
A.f필드는 두 번째Square메서드를 참조하는 대리자를 사용하여 초기화됩니다. 이 메서드는 매개 변수 목록과 정확히 일치하고DoubleFunc형식을 반환하기 때문입니다. 두 번째Square메서드가 없으면 컴파일 시간 오류가 발생했을 것입니다.예제 종료
12.8.18 typeof 연산자
typeof 연산자는 형식에 대한 System.Type 개체를 가져오는 데 사용됩니다.
typeof_expression
: 'typeof' '(' type ')'
| 'typeof' '(' unbound_type_name ')'
| 'typeof' '(' 'void' ')'
;
unbound_type_name
: identifier generic_dimension_specifier?
('.' identifier generic_dimension_specifier?)*
| unbound_qualified_alias_member
('.' identifier generic_dimension_specifier?)*
;
unbound_qualified_alias_member
: identifier '::' identifier generic_dimension_specifier?
;
generic_dimension_specifier
: '<' comma* '>'
;
comma
: ','
;
typeof_expression 첫 번째 형식은 typeof 키워드와 괄호 형식으로 구성됩니다. 이 형태의 식 결과는 지정된 형식에 대한 System.Type 객체입니다. 지정된 형식에 대해 하나의 System.Type 개체만 있습니다. 즉, T형식의 경우 typeof(T) == typeof(T) 항상 true입니다. 형식은 dynamic이 될 수 없습니다.
두 번째 형식의 typeof_expression은 typeof 키워드에 이어 괄호가 포함된 unbound_type_name로 구성됩니다.
참고: unbound_type_name 및 unbound_qualified_alias_member의 문법은 type_name(§7.8) 및 qualified_alias_member(§14.8.1)의 문법을 따르며, generic_dimension_specifier가 type_argument_list로 대체된다는 점을 제외합니다. 끝 메모
typeof_expression의 피연산자를 인식할 때, unbound_type_name 및 type_name 둘 다 적용 가능한 경우, 즉 generic_dimension_specifier나 type_argument_list를 포함하지 않는 경우라면, type_name이 선택되어야 합니다.
참고: ANTLR은 typeof_expression 대체 항목의 순서로 인해 지정된 선택을 자동으로 수행합니다. 끝 메모
unbound_type_name 의미는 다음과 같이 결정됩니다.
- 토큰 시퀀스는 각각의 generic_dimension_specifier를 동일한 수의 쉼표와 키워드 를 가진 type_argument_list로 대체하여
object으로 변환됩니다. - 결과 type_name 생성된 형식(§7.8)으로 확인됩니다.
- 그런 다음 unbound_type_name은 해결된 구성된 형식(§8.4)과 연관된 언바운드 제네릭 형식입니다.
참고: 구현에서 토큰 시퀀스를 변환하거나 중간 생성 형식을 생성할 필요가 없습니다. 단, 결정된 바인딩되지 않은 제네릭 형식이 이 프로세스를 따르는 것처럼"됩니다. 끝 메모
형식 이름이 nullable 참조 형식이 되는 것은 오류입니다.
typeof_expression의 결과는 언바운드 제네릭 타입에 대한 System.Type 개체입니다.
세 번째 형식의 typeof_expressiontypeof 키워드와 괄호가 있는 void 키워드로 구성됩니다. 이 폼의 식 결과는 형식이 없음을 나타내는 System.Type 개체입니다.
typeof(void)에 의해 반환된 형식 객체는 어떤 형식에 대해 반환된 형식 객체와도 다릅니다.
참고: 이 특수
System.Type개체는 언어의 메서드에 대한 리플렉션을 허용하는 클래스 라이브러리에서 유용합니다. 여기서 해당 메서드는void인스턴스를 사용하여System.Type메서드를 비롯한 모든 메서드의 반환 형식을 나타내는 방법을 갖습니다. 끝 메모
typeof 연산자는 형식 매개 변수에 사용할 수 있습니다. 형식 이름이 nullable 참조 형식으로 알려진 경우 컴파일 시간 오류입니다. 결과는 형식 매개 변수에 바인딩된 런타임 형식의 System.Type 객체입니다. 런타임 형식이 nullable 참조 형식인 경우 결과는 null이 아닌 대응하는 참조 형식입니다.
typeof 연산자는 생성된 형식 또는 바인딩되지 않은 제네릭 형식(§8.4.4)에서도 사용할 수 있습니다. 바인딩되지 않은 제네릭 형식의 System.Type 개체는 인스턴스 형식의 System.Type 개체(§15.3.2)와 다릅니다. 인스턴스 형식은 항상 런타임에 닫힌 생성 형식이므로 System.Type 개체는 사용 중인 런타임 형식 인수에 따라 달라집니다. 반면에 바인딩되지 않은 제네릭 형식에는 형식 인수가 없으며 런타임 형식 인수에 관계없이 동일한 System.Type 개체를 생성합니다.
예제: 예제
class X<T> { public static void PrintTypes() { Type[] t = { typeof(int), typeof(System.Int32), typeof(string), typeof(double[]), typeof(void), typeof(T), typeof(X<T>), typeof(X<X<T>>), typeof(X<>) }; for (int i = 0; i < t.Length; i++) { Console.WriteLine(t[i]); } } } class Test { static void Main() { X<int>.PrintTypes(); } }는 다음 출력을 생성합니다.
System.Int32 System.Int32 System.String System.Double[] System.Void System.Int32 X`1[System.Int32] X`1[X`1[System.Int32]] X`1[T]
int및System.Int32동일한 형식입니다.typeof(X<>)결과는 형식 인수에 의존하지 않지만typeof(X<T>)결과는 다릅니다.예제 종료
12.8.19 sizeof 연산자
sizeof 연산자는 지정된 형식의 변수가 차지하는 8비트 바이트 수를 반환합니다. sizeof에 피연산자로 지정된 형식은 unmanaged_type(§8.8)이어야 합니다.
sizeof_expression
: 'sizeof' '(' unmanaged_type ')'
;
미리 정의된 특정 형식의 경우 sizeof 연산자는 아래 표와 같이 상수 int 값을 생성합니다.
| 표현 | 결과 |
|---|---|
sizeof(sbyte) |
1 |
sizeof(byte) |
1 |
sizeof(short) |
2 |
sizeof(ushort) |
2 |
sizeof(int) |
4 |
sizeof(uint) |
4 |
sizeof(long) |
8 (여덟) |
sizeof(ulong) |
8 (여덟) |
sizeof(char) |
2 |
sizeof(float) |
4 |
sizeof(double) |
8 (여덟) |
sizeof(bool) |
1 |
sizeof(decimal) |
16 |
T열거형 형식의 경우 식 sizeof(T) 결과는 위에서 설명한 대로 해당 기본 형식의 크기와 동일한 상수 값입니다. 다른 모든 피연산자 형식의 sizeof 경우 연산자는 §24.6.9에 지정됩니다.
12.8.20 선택 및 선택되지 않은 연산자
checked 및 unchecked 연산자는 정수 형식 산술 연산 및 변환에 대한 오버플로 검사 컨텍스트를 제어하는 데 사용됩니다.
checked_expression
: 'checked' '(' expression ')'
;
unchecked_expression
: 'unchecked' '(' expression ')'
;
checked 연산자는 확인된 컨텍스트에서 포함된 식을 평가하고 unchecked 연산자는 확인되지 않은 컨텍스트에서 포함된 식을 평가합니다.
checked_expression 또는 unchecked_expression는 지정된 오버플로 검사 컨텍스트에서 포함된 식이 평가된다는 점만 제외하면 parenthesized_expression(§12.8.5)와 정확히 일치합니다.
오버플로 검사 컨텍스트는 checked 및 unchecked 문(§13.12)을 통해 제어할 수도 있습니다.
체크됨 및 체크되지 않음 연산자 및 문에 의해 설정된 오버플로 검사 문맥의 영향을 받는 연산은 다음과 같습니다.
- 피연산자가 정수 또는 열거형 형식인 경우 미리 정의된
++--연산자(§12.8.16 및 §12.9.7)입니다. - 피연산자가 정수 계열 형식인 경우, 미리 정의된 단항 연산자
-(§12.9.3)입니다. - 두 피연산자가 모두 정수 또는 열거형 형식인 경우 미리 정의된
+,-*및/이진 연산자(§12.12)입니다. - 한 정수 또는 열거형 형식에서 다른 정수 또는 열거형 형식으로 또는 또는
float정수 또는 열거형 형식으로 명시적 숫자 변환(double)입니다.
위의 작업 중 하나가 대상 형식에 표시하기에는 너무 큰 결과를 생성하는 경우 작업이 수행되는 컨텍스트가 결과 동작을 제어합니다.
-
checked컨텍스트에서 작업이 상수 식(§12.25)인 경우 컴파일 시간 오류가 발생합니다. 그렇지 않으면, 런타임에 작업이 수행될 때System.OverflowException오류가 던져집니다. -
unchecked컨텍스트에서는 대상 형식에 맞지 않는 모든 상위 비트를 삭제하여 결과가 잘립니다.
비 상수 식(§12.25)(런타임에 계산되는 식)의 경우 외부 요소(예: 컴파일러 스위치 및 실행 환경 구성)가 확인된 평가를 호출하지 checkedunchecked 않는 한 기본 오버플로 검사 컨텍스트는 선택되지 않습니다.
상수 식(§12.25)(컴파일 시간에 완전히 평가할 수 있는 식)의 경우 기본 오버플로 검사 컨텍스트가 항상 선택됩니다. 상수 식이 unchecked 컨텍스트에 명시적으로 배치되지 않는 한 식의 컴파일 시간 평가 중에 발생하는 오버플로는 항상 컴파일 시간 오류를 발생합니다.
익명 함수의 본문은 익명 함수가 발생하는 checked 또는 unchecked 컨텍스트의 영향을 받지 않습니다.
예제: 다음 코드에서
class Test { static readonly int x = 1000000; static readonly int y = 1000000; static int F() => checked(x * y); // Throws OverflowException static int G() => unchecked(x * y); // Returns -727379968 static int H() => x * y; // Depends on default }컴파일 시간에 두 식을 모두 평가할 수 없으므로 컴파일 시간 오류가 보고되지 않습니다. 런타임에
F메서드는System.OverflowException을 던지고,G메서드는 –727379968(범위를 벗어난 결과의 하위 32비트)를 반환합니다.H메서드의 동작은 컴파일에 대한 기본 오버플로 검사 컨텍스트에 따라 달라지지만F동일하거나G동일합니다.예제 종료
예제: 다음 코드에서
class Test { const int x = 1000000; const int y = 1000000; static int F() => checked(x * y); // Compile-time error, overflow static int G() => unchecked(x * y); // Returns -727379968 static int H() => x * y; // Compile-time error, overflow }
F및H에서 상수 식을 평가할 때 발생하는 오버플로가checked컨텍스트에서 표현식이 평가됨에 따라 컴파일 시간 오류를 일으킵니다. 오버플로는G상수 식을 평가할 때도 발생하지만unchecked컨텍스트에서 계산이 수행되므로 오버플로가 보고되지 않습니다.예제 종료
checked 및 unchecked 연산자는 "(" 및 ")" 토큰에 텍스트로 포함된 작업의 오버플로 검사 컨텍스트에만 영향을 줍니다. 연산자는 포함된 식을 평가한 결과로 호출되는 함수 멤버에 영향을 주지 않습니다.
예제: 다음 코드에서
class Test { static int Multiply(int x, int y) => x * y; static int F() => checked(Multiply(1000000, 1000000)); }F에서
checked사용은x * y의Multiply평가에 영향을 주지 않으므로,x * y가 기본 오버플로 검사 컨텍스트에서 평가됩니다.예제 종료
unchecked 연산자는 부호 있는 정수 상수를 십육진수 표기법으로 작성할 때 편리합니다.
예제:
class Test { public const int AllBits = unchecked((int)0xFFFFFFFF); public const int HighBit = unchecked((int)0x80000000); }위의 두 16진수 상수는 모두
uint형식입니다. 상수가int범위를 벗어나기 때문에,unchecked연산자가 없을 경우int로 캐스팅하면 컴파일 시 오류가 발생합니다.예제 종료
참고:
checked및unchecked연산자와 문을 사용하면 프로그래머가 일부 숫자 계산의 특정 측면을 제어할 수 있습니다. 그러나 일부 숫자 연산자의 동작은 피연산자의 데이터 형식에 따라 달라집니다. 예를 들어 두 개의 소수 자릿수를 곱하면 명시적으로 확인되지 않은 구문 내에서도 오버플로에 대한 예외가 항상 발생합니다. 마찬가지로, 두 개의 부동 소수점 곱하기는 명시적으로 확인된 구문 내에서도 오버플로에 대한 예외를 초래하지 않습니다. 또한 다른 연산자는 기본값이든 명시적이든 검사 모드의 영향을 받지 않습니다. 끝 메모
12.8.21 기본값 식
기본값 식은 형식의 기본값(§9.3)을 가져오는 데 사용됩니다.
default_value_expression
: explicitly_typed_default
| default_literal
;
explicitly_typed_default
: 'default' '(' type ')'
;
default_literal
: 'default'
;
default_literal은 기본값(§9.3)을 나타냅니다. 형식은 없지만 기본 리터럴 변환(§10.2.16)을 통해 모든 형식으로 변환할 수 있습니다.
default_value_expression 결과는 explicitly_typed_default 명시적 형식의 기본값(§9.3) 또는 default_value_expression 대한 변환의 대상 형식입니다.
default_value_expression 형식이 다음 중 하나인 경우 상수 식(§12.25)입니다.
- 참조 형식
- 참조 형식으로 알려진 형식 매개 변수(§8.2);
- 값 형식 중 하나:
sbyte,byte,short,ushort,int,uint,long,ulong,char,float,double,decimal,bool,; 또는 - 어떤 열거형 형식.
12.8.22 스택 할당
스택 할당 식은 실행 스택에서 메모리 블록을 할당합니다. 실행 스택 지역 변수가 저장되는 메모리 영역입니다. 실행 스택은 관리되는 힙의 일부가 아닙니다. 로컬 변수 스토리지에 사용되는 메모리는 현재 함수가 반환될 때 자동으로 복구됩니다.
스택 할당 식에 대한 안전한 컨텍스트 규칙은 §16.4.15.7에 설명되어 있습니다.
stackalloc_expression
: 'stackalloc' unmanaged_type '[' expression ']'
| 'stackalloc' unmanaged_type? '[' constant_expression? ']' stackalloc_initializer
;
stackalloc_initializer
: '{' stackalloc_initializer_element_list '}'
;
stackalloc_initializer_element_list
: stackalloc_element_initializer (',' stackalloc_element_initializer)* ','?
;
stackalloc_element_initializer
: expression
;
unmanaged_type(§8.8)는 새로 할당된 위치에 저장될 항목의 유형을 나타내며, 표현식는 이러한 항목의 수를 나타냅니다. 이를 종합하면 필요한 할당 크기를 지정합니다.
식 형식은 int형식으로 암시적으로 변환할 수 있어야 합니다.
스택 할당 크기는 음수가 될 수 없기 때문에 항목 수를 음수 값으로 나타내는 constant_expression을 지정하면 컴파일 오류가 발생합니다.
런타임에 할당할 항목 수가 음수 값이면 동작이 정의되지 않습니다. 0이면 할당이 이루어지지 않고 반환되는 값이 구현 정의됩니다. 메모리가 부족하여 항목을 할당할 수 없는 경우 System.StackOverflowException throw됩니다.
stackalloc_initializer이 있는 경우,
- unmanaged_type 생략하면 stackalloc_element_initializer 집합에 대한 가장 일반적인 형식(§12.6.3.16)에 대한 규칙에 따라 유추됩니다.
- constant_expression 생략될 경우 stackalloc_element_initializer개수로 유추됩니다.
- constant_expression이 존재하면 stackalloc_element_initializer의 수와 같아야 합니다.
각 stackalloc_element_initializer는 unmanaged_type(§10.2)로 암시적 변환을 가져야 합니다. stackalloc_element_initializer인덱스 0의 요소부터 시작하여 할당된 메모리의 요소를 증가하는 순서로 초기화합니다. stackalloc_initializer없는 경우 새로 할당된 메모리의 콘텐츠가 정의되지 않습니다.
stackalloc_expressionlocal_variable_declaration(§13.6.2)의 초기화 식으로 직접 발생하는 경우 local_variable_type 포인터 형식(§24.3) 또는 유추(var)인 경우 stackalloc_expression 결과는 형식 T* 의 포인터(§24.9)입니다. 이 경우 stackalloc_expression 안전하지 않은 코드에 표시되어야 합니다. 그 외에는 stackalloc_expression의 결과가 Span<T>형식의 인스턴스입니다: 여기서 T는 unmanaged_type입니다.
-
Span<T>(§C.3)는 ref 구조체 형식(§16.2.3)이며, 여기서는 stackalloc_expression할당된 블록인 메모리 블록을 형식화된(T) 항목의 인덱싱 가능한 컬렉션으로 표시합니다. - 결과의
Length속성은 할당된 항목 수를 반환합니다. - 결과 인덱서(§15.9)는 할당된 블록의 항목에 variable_reference(§9.5)를 반환하고 범위를 확인합니다.
스택 할당 이니셜라이저는 catch 또는 finally 블록에서 허용되지 않습니다(§13.11).
참고:
stackalloc사용하여 할당된 메모리를 명시적으로 해제할 수 있는 방법은 없습니다. 끝 메모
함수 멤버를 실행하는 동안 생성된 모든 스택 할당 메모리 블록은 해당 함수 멤버가 반환될 때 자동으로 삭제됩니다.
stackalloc 연산자를 제외하고 C#은 가비지 수집되지 않은 메모리를 관리하기 위한 미리 정의된 구문을 제공하지 않습니다. 이러한 서비스는 일반적으로 클래스 라이브러리를 지원하거나 기본 운영 체제에서 직접 가져옵니다.
예제:
// Memory uninitialized Span<int> span1 = stackalloc int[3]; // Memory initialized Span<int> span2 = stackalloc int[3] { -10, -15, -30 }; // Type int is inferred Span<int> span3 = stackalloc[] { 11, 12, 13 }; // Error; result is int*, not allowed in a safe context var span4 = stackalloc[] { 11, 12, 13 }; // Error; no conversion from Span<int> to Span<long> Span<long> span5 = stackalloc[] { 11, 12, 13 }; // Converts 11 and 13, and returns Span<long> Span<long> span6 = stackalloc[] { 11, 12L, 13 }; // Converts all and returns Span<long> Span<long> span7 = stackalloc long[] { 11, 12, 13 }; // Implicit conversion of Span<T> ReadOnlySpan<int> span8 = stackalloc int[] { 10, 22, 30 }; // Implicit conversion of Span<T> Widget<double> span9 = stackalloc double[] { 1.2, 5.6 }; public class Widget<T> { public static implicit operator Widget<T>(Span<double> sp) { return null; } }
span8의 경우,stackalloc이Span<int>를 발생시키고 이는 암시적 연산자에 의해ReadOnlySpan<int>로 변환됩니다. 마찬가지로span9경우 결과Span<double>표시된 것처럼 변환을 사용하여Widget<double>사용자 정의 형식으로 변환됩니다. 예제 종료
12.8.23 nameof 연산자
nameof_expression는 프로그램 요소의 이름을 상수 문자열로 가져오는 데 사용됩니다.
nameof_expression
: 'nameof' '(' named_entity ')'
;
named_entity
: named_entity_target ('.' identifier type_argument_list?)*
;
named_entity_target
: simple_name
| 'this'
| 'base'
| predefined_type
| qualified_alias_member
;
nameof가 키워드가 아니기 때문에, nameof_expression는 항상 단순 이름 nameof의 호출과 구문적으로 모호합니다. 호환성 문제로, 이름 이름 조회(nameof)가 성공하면 호출이 유효한지 여부와 관계없이 해당 식은 호출_표현식으로 처리됩니다. 그렇지 않으면 nameof_expression입니다.
간단한 이름 및 멤버 액세스 조회는 컴파일 시간에 named_entity 수행되며, §12.8.4 및 §12.8.7설명된 규칙에 따라 수행됩니다. 그러나 §12.8.4 및 §12.8.7에 설명된 검색에서 인스턴스 멤버가 정적 컨텍스트에서 발견되었기 때문에 오류가 발생하는 경우와 달리, nameof_expression에서는 이러한 오류가 발생하지 않습니다.
메서드 그룹을 지정하는 named_entity에 type_argument_list가 있는 것은 컴파일 시간 오류입니다.
named_entity_target에 dynamic유형이 있는 것은 컴파일 시간 오류입니다.
nameof_expressionstring형식의 상수 식이며 런타임에는 영향을 주지 않습니다. 특히 named_entity 평가되지 않으며 명확한 할당 분석(§9.4.4.22)을 위해 무시됩니다. 그 값은 다음과 같이 변형된 선택적 최종 type_argument_list이전의 named_entity의 마지막 식별자입니다.
- 접두사 "
@"(사용되는 경우)가 제거됩니다. - 각 unicode_escape_sequence는 해당 유니코드 문자로 변환됩니다.
- 모든 형식 문자이 제거됩니다.
식별자 간의 같음을 테스트할 때 §6.4.3 적용된 것과 동일한 변환입니다.
예제: 다음은
nameof네임스페이스 내에 선언된 제네릭 형식List<T>을 가정할 때, 다양한System.Collections.Generic식의 결과를 설명합니다.using TestAlias = System.String; class Program { static void Main() { var point = (x: 3, y: 4); string n1 = nameof(System); // "System" string n2 = nameof(System.Collections.Generic); // "Generic" string n3 = nameof(point); // "point" string n4 = nameof(point.x); // "x" string n5 = nameof(Program); // "Program" string n6 = nameof(System.Int32); // "Int32" string n7 = nameof(TestAlias); // "TestAlias" string n8 = nameof(List<int>); // "List" string n9 = nameof(Program.InstanceMethod); // "InstanceMethod" string n10 = nameof(Program.GenericMethod); // "GenericMethod" string n11 = nameof(Program.NestedClass); // "NestedClass" // Invalid // string x1 = nameof(List<>); // Empty type argument list // string x2 = nameof(List<T>); // T is not in scope // string x3 = nameof(GenericMethod<>); // Empty type argument list // string x4 = nameof(GenericMethod<T>); // T is not in scope // string x5 = nameof(int); // Keywords not permitted // Type arguments not permitted for method group // string x6 = nameof(GenericMethod<Program>); } void InstanceMethod() { } void GenericMethod<T>() { string n1 = nameof(List<T>); // "List" string n2 = nameof(T); // "T" } class NestedClass { } }이 예제에서 잠재적으로 예상 밖일 수 있는 부분은 전체 네임스페이스 대신 "Generic"으로
nameof(System.Collections.Generic)를 해석하고, "String" 대신 "TestAlias"로nameof(TestAlias)이 해석된다는 점입니다. 예제 종료
12.8.24 익명 메서드 식
익명 메서드 표현식은 익명 함수를 정의하는 두 가지 방법 중 하나입니다. 이러한 내용은 §12.21에 자세히 설명되어 있습니다.
12.9 단항 연산자
12.9.1 일반
+, -! (논리 부정 §12.9.4만 해당), ~, ^, ++, --캐스트 및 await 연산자를 단항 연산자라고 합니다.
참고: 컴파일 시간에만 작동하고 오버로드할 수 없는 특성으로 인해 접미사 null-허용 연산자(§12.8.9),
!위 목록에서 제외됩니다. 끝 메모
unary_expression
: primary_expression
| '+' unary_expression
| '-' unary_expression
| logical_negation_operator unary_expression
| '~' unary_expression
| '^' unary_expression
| pre_increment_expression
| pre_decrement_expression
| cast_expression
| await_expression
| pointer_indirection_expression // unsafe code support
| addressof_expression // unsafe code support
;
pointer_indirection_expression (§24.6.2) 및 addressof_expression (§24.6.5)는 안전하지 않은 코드(§24)에서만 사용할 수 있습니다.
unary_expression 피연산자의 컴파일 시간 형식이 dynamic이면, 이는 동적으로 바인딩됩니다(§12.3.3). 이 경우 다음과 같습니다.
-
unary_expression 컴파일 시간 형식은 다음과 같습니다.
-
Index인덱스 from-end 연산자의^경우(§12.9.6) -
dynamic다른 모든 단항 연산자 그리고
-
- 아래에 설명된 해상도는 런타임에 피연산자의 런타임 형식을 사용하여 수행됩니다.
12.9.2 단항 플러스 연산자
양식 +x연산의 경우 단항 연산자 오버로드 확인(§12.4.4)이 적용되어 특정 연산자 구현을 선택합니다. 피연산자는 선택한 연산자의 매개 변수 형식으로 변환되고 결과의 형식은 연산자의 반환 형식입니다. 미리 정의된 단항 더하기 연산자는 다음과 같습니다.
int operator +(int x);
uint operator +(uint x);
long operator +(long x);
ulong operator +(ulong x);
float operator +(float x);
double operator +(double x);
decimal operator +(decimal x);
이러한 각 연산자의 결과는 피연산자의 값일 뿐입니다.
승격된(§12.4.8) 형태의 미리 정의된 단항 플러스 연산자 및 승격되지 않은 형태도 미리 정의되어 있습니다.
12.9.3 단항 음수 연산자
양식 –x연산의 경우 단항 연산자 오버로드 확인(§12.4.4)이 적용되어 특정 연산자 구현을 선택합니다. 피연산자는 선택한 연산자의 매개 변수 형식으로 변환되고 결과의 형식은 연산자의 반환 형식입니다. 미리 정의된 단항 빼기 연산자는 다음과 같습니다.
정수 부정:
int operator –(int x); long operator –(long x);결과는 0에서
X빼서 계산됩니다.X값이 피연산자 형식의 가장 작은 표현 가능 값(-2³¹ forint또는 -2⁶³ forlong)인 경우,X의 수학적 부정은 피연산자 형식 내에서 표현할 수 없습니다.checked컨텍스트 내에서 이 문제가 발생하면System.OverflowException이 발생합니다.unchecked컨텍스트 내에서 발생하는 경우 결과는 피연산자의 값이며 오버플로는 보고되지 않습니다.부정 연산자의 피연산자가
uint형식이면long형식으로 변환되고 결과의 형식은long. 예외는int값−2147483648(-2의 31제곱)을 10진수 정수 리터럴(§6.4.5.3)로 작성할 수 있도록 허용하는 규칙입니다.부정 연산자의 피연산자가
ulong형식이면 컴파일 시간 오류가 발생합니다. 예외는long값−9223372036854775808(-2⁶³)을 10진수 정수 리터럴(§6.4.5.3)로 작성할 수 있도록 허용하는 규칙입니다.부동 소수점 부정:
float operator –(float x); double operator –(double x);결과는 해당 기호가 반전된
X값입니다.x이NaN이면, 결과는NaN이다.10진수 부정:
decimal operator –(decimal x);결과는 0에서
X빼서 계산됩니다. 10진수 부정은System.Decimal형식의 단항 빼기 연산자를 사용하는 것과 같습니다.
승격된(§12.4.8) 위에 정의된 승격되지 않은 미리 정의된 단항 빼기 연산자의 형식도 미리 정의됩니다.
12.9.4 논리 부정 연산자
양식 !x연산의 경우 단항 연산자 오버로드 확인(§12.4.4)이 적용되어 특정 연산자 구현을 선택합니다. 피연산자는 선택한 연산자의 매개 변수 형식으로 변환되고 결과의 형식은 연산자의 반환 형식입니다. 미리 정의된 논리 부정 연산자는 하나만 존재합니다.
bool operator !(bool x);
이 연산자는 피연산자의 논리적 부정을 계산합니다. 피연산자가 true경우 결과가 false. 피연산자가 false이면, 결과는 true입니다.
리프티드 (§12.4.8) 형태의 미리 정의된 논리 부정 연산자 역시 미리 정의되어 있습니다.
참고: 접두사 논리 부정 연산자 및 접미사 null 용서 연산자(§12.8.9)는 동일 어휘 토큰(!)으로 표현되지만 서로 다릅니다.
끝 메모
12.9.5 비트 반전 연산자
양식 ~x연산의 경우 단항 연산자 오버로드 확인(§12.4.4)이 적용되어 특정 연산자 구현을 선택합니다. 피연산자는 선택한 연산자의 매개 변수 형식으로 변환되고 결과의 형식은 연산자의 반환 형식입니다. 미리 정의된 비트 보수 연산자는 다음과 같습니다.
int operator ~(int x);
uint operator ~(uint x);
long operator ~(long x);
ulong operator ~(ulong x);
이러한 각 연산자의 경우 연산 결과는 x의 비트 반전입니다.
모든 열거형 형식 E은 암시적으로 다음과 같은 비트 보수 연산자를 제공합니다.
E operator ~(E x);
~x의 결과는, X가 E라는 기본 형식을 가진 열거형 타입의 표현식일 때, U를 평가한 결과와 정확히 동일합니다. 단, (E)(~(U)x)로의 변환은 항상 E 컨텍스트(unchecked)에서 수행되는 것처럼 수행됩니다.
리프팅된 (§12.4.8) 미리 정의된 비트 보수 연산자의 형태도 위에서 정의된 것처럼 미리 정의되어 있습니다.
12.9.6 인덱스 from-end 연산자
단항 ^ 연산자는 인 덱스 from-end 연산 자(구어체로 hat 연산자라고 함)라고 합니다. 이 연산자는 오버로드할 수 없으며(§12.4.3) 미리 정의된 연산자가 하나 있습니다.
Index operator ^(int x);
양식 ^x 의 연산 결과는 식의 결과와 동일한 from-end Index (§18.2) 값입니다.
new Index(x, true)
다른 unary_expression마찬가지로 피연산자는 컴파일 시간 형식 dynamic (§12.9.1)을 가지며 동적으로 바인딩될 수 있습니다(§12.3.3). 결과의 컴파일 시간 형식은 항상 Index.입니다.
인덱스 from-end 연산자의 리프트된(§12.4.8) 형식도 미리 정의되어 있습니다.
12.9.7 접두사 증가 및 감소 연산자
pre_increment_expression
: '++' unary_expression
;
pre_decrement_expression
: '--' unary_expression
;
접두사 증가 또는 감소 작업의 피연산자는 변수, 속성 액세스 또는 인덱서 액세스로 분류된 식이어야 합니다. 작업의 결과는 피연산자와 동일한 형식의 값입니다.
접두사 증가 또는 감소 작업의 피연산자가 속성 또는 인덱서 액세스인 경우 속성 또는 인덱서에는 get 및 set 접근자가 둘 다 있어야 합니다. 그렇지 않으면 바인딩 시간 오류가 발생합니다.
단항 연산자 오버로드 확인(§12.4.4)이 적용되어 특정 연산자 구현을 선택합니다. 미리 정의된 ++ 및 -- 연산자는 sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal및 열거형 형식에 대해 존재합니다. 미리 정의된 ++ 연산자는 피연산자에서 1 추가하여 생성된 값을 반환하고, 미리 정의된 -- 연산자는 피연산자에서 1 빼서 생성된 값을 반환합니다.
checked 컨텍스트에서 이 추가 또는 빼기의 결과가 결과 형식의 범위를 벗어난다면, 그리고 결과 형식이 정수 형식 또는 열거형 타입이라면, System.OverflowException 예외가 발생합니다.
선택한 단항 연산자의 반환 형식에서 unary_expression형식으로 암시적 변환이 있어야 합니다. 그렇지 않으면 컴파일 시간 오류가 발생합니다.
++x 또는 --x 폼의 접두사 증가 또는 감소 작업의 런타임 처리는 다음 단계로 구성됩니다.
-
x변수로 분류되는 경우:-
x변수를 생성하도록 평가됩니다. -
x값은 선택한 연산자의 피연산자 형식으로 변환되고 연산자는 이 값을 인수로 사용하여 호출됩니다. - 연산자가 반환하는 값은
x형식으로 변환됩니다. 결과 값은x평가에 의해 지정된 위치에 저장되고 작업의 결과가 됩니다.
-
-
x속성 또는 인덱서 액세스 권한으로 분류되는 경우:- 인스턴스 식(
x이static이 아닌 경우) 및x에 연결된 인수 목록(x가 인덱서 액세스인 경우)이 평가되고, 그 결과는 후속 get 및 set 접근자 호출에서 사용됩니다. -
x의 get 접근자가 호출되었습니다. - get 접근자가 반환하는 값은 선택한 연산자의 피연산자 형식으로 변환되고 이 값을 인수로 사용하여 연산자가 호출됩니다.
- 연산자가 반환하는 값은
x형식으로 변환됩니다.x의 set 접근자는 이 값을 값 인수로 사용하여 호출됩니다. - 이 값은 작업의 결과도 됩니다.
- 인스턴스 식(
++ 및 -- 연산자는 접두사 표기법(§12.8.16)도 지원합니다.
x++ 또는 x-- 결과는 작업 전의 x 값인 반면, ++x 또는 --x 결과는 작업 후 x 값입니다. 두 경우 모두 x 자체에는 작업 후 동일한 값이 있습니다.
연산자 ++ 또는 연산자 -- 구현은 후위 또는 전위 표기법을 사용하여 호출할 수 있습니다. 두 표기법은 별도의 연산자 구현을 사용할 수 없습니다.
리프트된(§12.4.8) 위에서 정의된 미리 정의된 접두사 증가 및 감소 연산자의 형태 또한 미리 정의되어 있습니다.
12.9.8 캐스트 식
cast_expression 식은 주어진 형식으로 명시적으로 식을 변환하는 데 사용됩니다.
cast_expression
: '(' type ')' unary_expression
;
형식 (형식이 (T)E이고 T이(가) E인 경우)의 cast_expression는 의 값을 형식 E으로 명시적으로 변환합니다(T참조).
E에서 T로의 명시적 변환이 없으면 바인딩 시간 오류가 발생합니다. 그렇지 않은 경우 결과는 명시적 변환에 의해 생성되는 값입니다.
E 변수를 나타내는 경우에도 결과는 항상 값으로 분류됩니다.
cast_expression의 문법은 특정 구문상의 모호성을 초래합니다.
예제: 식
(x)–y는 를–y형식으로 캐스트하는x으로 해석되거나 괄호_식과 결합된 덧셈_식(값x – y를 계산)으로 해석될 수 있습니다. 예제 종료
cast_expression 모호성을 해결하기 위해 괄호로 묶인 하나 이상의 토큰(§6.4)의 시퀀스는 다음 중 하나 이상이 true인 경우에만 cast_expression 시작으로 간주됩니다.
- 토큰 시퀀스는 형식에 대한 올바른 문법이지만 식에는 올바른 문법이 아닙니다.
- 토큰 시퀀스는 형식에 대해 올바른 문법이며, 닫는 괄호 바로 다음에 있는 토큰은 토큰 “
~”, 토큰 “!”, 토큰 “(”, 식별자(§6.4.3), 리터럴(§6.4.5), 또는 및as을 제외한 모든 키워드(is)입니다.
위의 "올바른 문법"이라는 용어는 토큰 시퀀스가 특정 문법 프로덕션을 준수해야 함을 의미합니다. 특히 구성 요소 식별자의 실제 의미를 고려하지 않습니다.
예제:
x및y식별자이면x.y실제로 형식을 나타내지 않더라도x.y형식에 대한 올바른 문법입니다. 예제 종료
참고: 명확성 규칙에 따르면,
x및y이 식별자인 경우,(x)y,(x)(y)및(x)(-y)은 캐스트 표현식이지만,(x)-y이 형식을 식별하더라도x는 그렇지 않습니다. 그러나x가 미리 정의된 형식(예:int)을 식별하는 키워드라면, 이때 4가지 형식은 모두 cast_expression입니다(이러한 키워드는 그 자체로 식이 될 수 없기 때문입니다). 끝 메모
12.9.9 Await 식
12.9.9.1 일반
await 연산자는 피연산자가 나타내는 비동기 작업이 완료될 때까지 둘러싼 비동기 함수의 평가를 일시 중단하는 데 사용됩니다.
await_expression
: 'await' unary_expression
;
await_expression 비동기 함수(§15.14)의 본문에서만 허용됩니다. 가장 가까운 바깥쪽 비동기 함수 내에서 await_expression이 다음 위치에는 있어서는 안 됩니다.
- 중첩된(비동기) 익명 함수 내부
- 블록 내부의 lock_statement
- 무명 함수에서 식 트리 형식으로 변환(§10.7.3)
- 안전하지 않은 컨텍스트에서
참고: query_expression내에서 await_expression은(는) 대다수의 경우 발생할 수 없습니다. 이는 해당 식들이 비동기 람다 식을 사용하도록 구문적으로 변환되기 때문입니다. 끝 메모
비동기 함수 내에서는 await을 available_identifier로 사용할 수 없지만, 축자 식별자 @await는 사용할 수 있습니다. 따라서 await_expression과 식별자와 관련된 다양한 표현들 사이에는 구문상의 모호성이 없습니다. 비동기 함수 외부에서는 await 일반 식별자 역할을 합니다.
await_expression의 피연산자는 작업이라고 불립니다. await_expression을 평가할 때, 완료되었을 수도 있고 완료되지 않았을 수도 있는 비동기 작업을 나타냅니다.
await 연산자의 목적은 대기 중인 작업이 완료될 때까지 바깥쪽 비동기 함수의 실행을 일시 중단한 다음 결과를 가져오는 것입니다.
12.9.9.2 대기 가능 식
await_expression의 작업은 반드시 대기 가능한해야 합니다. 다음 조건 중 하나를 충족하면 t 표현식은 대기할 수 있습니다.
-
t은 컴파일 시 타입dynamic입니다 -
t은(는) 매개변수나 형식 매개변수가 없는GetAwaiter이라는 액세스 가능한 인스턴스 또는 확장 메서드를 가지고 있으며, 반환 형식은A이며, 다음 모든 조건을 만족합니다:-
A는 인터페이스System.Runtime.CompilerServices.INotifyCompletion을 구현합니다 (간결성을 위해INotifyCompletion로 불리움). -
A에는IsCompleted형식의 액세스 가능하고 읽기 가능한 인스턴스 속성bool이 있습니다. -
A에는 매개변수와 형식 매개변수가 없는 접근 가능한 인스턴스 메서드GetResult이 있습니다.
-
GetAwaiter 메서드의 목적은 작업을 위한 awaiter를 얻는 것입니다. 대기식의 위해 A 형식이 대기자 형식로 불립니다.
IsCompleted 속성의 목적은 작업이 이미 완료되었는지 확인하는 것입니다. 그렇다면 평가를 일시 중단할 필요가 없습니다.
INotifyCompletion.OnCompleted 메서드의 목적은 작업에 대한 "후속 작업"을 등록하는 것입니다. 즉, 작업이 완료되면 호출되는 System.Action형식의 대리자를 말합니다.
GetResult 메서드의 목적은 작업이 완료되면 작업의 결과를 가져오는 것입니다. 이 결과는 성공적으로 완료되며 결과 값을 가지거나, 또는 GetResult 메서드에 의해 발생되는 예외일 수 있습니다.
12.9.9.3 await 식 분류
식 await t 식 (t).GetAwaiter().GetResult()동일한 방식으로 분류됩니다. 따라서 GetResult 반환 형식이 void인 경우, await_expression는 아무것으로도 분류됩니다.
void 반환 형식이 아닌 경우, T은 형식의 값으로 분류됩니다.
12.9.9.4 await 식의 런타임 평가
런타임에 식 await t는 다음과 같이 평가됩니다.
- 식
a을 평가하여 awaiter(t).GetAwaiter()가 얻어집니다. - 식
bool를 평가하여b(a).IsCompleted을 얻습니다. - 만약
b이false이라면, 평가는a가 인터페이스System.Runtime.CompilerServices.ICriticalNotifyCompletion(이하 간단히ICriticalNotifyCompletion로 호출) 을 구현하는지에 따라 달라집니다. 이 검사는 바인딩 시간에 수행됩니다. 즉,a가 컴파일 시간에 형식이dynamic인 경우에는 런타임에, 그렇지 않으면 컴파일 시간에 수행됩니다.r재개 대리자를 나타냅니다(§15.14).- 만약
a가ICriticalNotifyCompletion을 구현하지 않으면, 식((a) as INotifyCompletion).OnCompleted(r)가 평가됩니다. -
a가ICriticalNotifyCompletion을 구현하는 경우, 그때 식((a) as ICriticalNotifyCompletion).UnsafeOnCompleted(r)가 평가됩니다. - 그런 다음 평가가 일시 중단되고 컨트롤이 비동기 함수의 현재 호출자에게 반환됩니다.
- 만약
- 즉시(
b이true인 경우) 또는 나중에 다시 시작 대리자를 호출할 때(b이false인 경우), 식(a).GetResult()가 평가됩니다. 값을 반환하는 경우 해당 값은 await_expression결과입니다. 그렇지 않으면 결과는 아무것도 없습니다.
awaiter의 INotifyCompletion.OnCompleted 및 ICriticalNotifyCompletion.UnsafeOnCompleted 인터페이스 메서드 구현은 대리자 r가 최대 한 번만 호출되도록 해야 합니다. 그렇지 않으면 포함하는 비동기 함수의 동작이 미정의입니다.
12.10 Range 연산자
연 .. 산자를 범위 연산자라고 합니다.
range_expression
: unary_expression
| unary_expression? '..' unary_expression?
;
미리 정의된 범위 연산자는 다음과 같습니다.
Range operator ..(Index x, Index y);
범위 연산자는 오버로드할 수 없습니다(§12.4.3).
모든 범위 식은 다음과 같은 형식 x..y을 갖는 것으로 처리됩니다.
-
x가 있으면 왼쪽 피연산자이고, 그렇지 않으면 식0입니다. -
y가 있으면 오른쪽 피연산자이고, 그렇지 않으면 식^0입니다.
연산 결과는 식의 결과와 Range 동일한 (§18.3) 값입니다.
new Range(x, y)
범위 식의 피연산자 중 하나 또는 둘 다 컴파일 시간 형식 dynamic이 있는 경우 식은 동적으로 바인딩됩니다(§12.3.3). 결과의 컴파일 시간 형식은 항상 Range.입니다.
범위 연산자의 리프트된(§12.4.8) 형식도 미리 정의되어 있습니다.
범위 연산자가 비연관형(§12.4.2)입니다.
12.11 식 전환
switch_expression 식 컨텍스트에서 유사 의미 체계를 제공합니다switch.
switch_expression
: range_expression
| switch_expression 'switch' '{' switch_expression_arms? '}'
;
switch_expression_arms
: switch_expression_arm (',' switch_expression_arm)* ','?
;
switch_expression_arm
: pattern case_guard? '=>' switch_expression_arm_expression
;
switch_expression_arm_expression
: expression
;
스위치 식의 각 switch_expression_arm 모든 switch_expression_arm_expression 암시적 변환이 있는 경우 스위치 식에서 형식 T 으로 전환 식 변환(T)이 있습니다.
switch 식에 스위치 식 변환이 적용되지 않는 경우
- switch_expression 형식은 switch_expression_arm switch_expression_arm_expression 중 가장 일반적인 형식인 §12.6.3.16)이며, 이러한 형식이 있는 경우 각 switch_expression_arm_expression 해당 형식으로 암시적으로 변환할 수 있습니다.
- 이러한 형식이 없으면 오류입니다.
일부 이전 패턴과 가드가 항상 일치하므로 일부 switch_expression_arm 패턴이 결과에 영향을 줄 수 없는 경우 오류입니다.
스위치 식은 해당 입력의 모든 값이 스위치 식의 하나 이상의 팔에서 처리되는 경우 완전 이라고 합니다. 스위치 식이 완전하지 않으면 컴파일러에서 경고를 생성합니다.
런타임에 switch_expression 결과는 switch_expression왼쪽에 있는 식이 switch_expression_arm 패턴과 일치하고 switch_expression_armcase_guard 있는 경우 계산되는 첫 번째 switch_expression_arm 식의 값입니다.true 이러한 switch_expression_arm 없는 경우 switch_expression 예외 System.InvalidOperationException 인스턴스(또는 해당 인스턴스에서 파생된 클래스)를 throw합니다.
예제: 다음은 온라인 맵의 시각적 방향을 나타내는 열거형 값을 해당 카디널 길로 변환합니다.
static Orientation ToOrientation(Direction direction) => direction switch { Direction.Up => Orientation.North, Direction.Right => Orientation.East, Direction.Down => Orientation.South, Direction.Left => Orientation.West, _ => throw new ArgumentOutOfRangeException(direction.ToString()), }; public enum Direction { Up, Down, Right, Left } public enum Orientation { North, South, East, West }예제 종료
12.12 산술 연산자
12.12.1 일반
*, /, %, +및 - 연산자를 산술 연산자라고 합니다.
multiplicative_expression
: switch_expression
| multiplicative_expression '*' switch_expression
| multiplicative_expression '/' switch_expression
| multiplicative_expression '%' switch_expression
;
additive_expression
: multiplicative_expression
| additive_expression '+' multiplicative_expression
| additive_expression '-' multiplicative_expression
;
산술 연산자의 피연산자가 컴파일 시간 타입이 dynamic일 때, 식이 동적으로 바인딩됩니다(§12.3.3). 이 경우 식의 컴파일 시간 타입은 dynamic이며, 아래에 설명된 해상도는 컴파일 시간 타입이 dynamic인 피연산자의 런타임 타입을 사용하여 런타임에 처리됩니다.
12.12.2 곱하기 연산자
양식 x * y연산의 경우 이진 연산자 오버로드 확인(§12.4.5)이 적용되어 특정 연산자 구현을 선택합니다. 피연산자는 선택한 연산자의 매개 변수 형식으로 변환되고 결과 형식은 연산자의 반환 형식입니다.
미리 정의된 곱셈 연산자는 아래에 나열되어 있습니다. 연산자는 모두 x 및 y곱을 계산합니다.
정수 곱하기:
int operator *(int x, int y); uint operator *(uint x, uint y); long operator *(long x, long y); ulong operator *(ulong x, ulong y);checked컨텍스트에서 제품이 결과 형식의 범위를 벗어나면System.OverflowException이 발생합니다.unchecked컨텍스트에서는 오버플로가 보고되지 않으며 결과 형식 범위를 벗어난 중요한 상위 비트는 삭제됩니다.부동 소수점 곱하기:
float operator *(float x, float y); double operator *(double x, double y);제품은 IEC 60559 산술 연산의 규칙에 따라 계산됩니다. 다음 표에서는 0이 아닌 유한 값, 0, 무한대 및 NaN의 가능한 모든 조합의 결과를 나열합니다. 테이블에서
x및y양수 유한 값입니다.z은x * y의 결과로, 가장 가까운 표현 가능한 값으로 반올림된 것입니다. 결과의 크기가 대상 형식에 비해 너무 크면z무한대입니다. 반올림으로 인해z이 0일 수 있으며, 심지어x과y가 모두 0이 아닐 때에도 그렇습니다.+y-y+0-0+∞-∞NaN+x+z-z+0-0+∞-∞NaN-x-z+z-0+0-∞+∞NaN+0+0-0+0-0NaNNaNNaN-0-0+0-0+0NaNNaNNaN+∞+∞-∞NaNNaN+∞-∞NaN-∞-∞+∞NaNNaN-∞+∞NaNNaNNaNNaNNaNNaNNaNNaNNaN(달리 언급된 경우를 제외하고 § 12.12.2–§12.12.6 의 부동 소수점 테이블에서 "
+"를 사용하면 값이 양수이고"-사용은 값이 음수임을 의미하며 부호가 없으면 값이 양수이거나 음수이거나 부호(NaN)가 없음을 의미합니다.10진수 곱하기:
decimal operator *(decimal x, decimal y);결과값의 크기가 너무 커서 10진수 형식으로 표현할 수 없으면
System.OverflowException오류가 발생합니다. 반올림으로 인해 피연산자 중 어느 것도 0이 아니더라도 결과가 0이 될 수 있습니다. 반올림하기 전에 결과의 배율은 두 피연산자의 배율 합계입니다. 10진수 곱셈은System.Decimal형식의 곱하기 연산자를 사용하는 것과 같습니다.
리프트된(§12.4.8) 형태의 미리 정의된 곱셈 연산자는 비리프트된 형태와 함께 또한 미리 정의되어 있습니다.
12.12.3 Division 연산자
양식 x / y연산의 경우 이진 연산자 오버로드 확인(§12.4.5)이 적용되어 특정 연산자 구현을 선택합니다. 피연산자는 선택한 연산자의 매개 변수 형식으로 변환되고 결과 형식은 연산자의 반환 형식입니다.
미리 정의된 나누기 연산자는 다음과 같습니다. 연산자는 모두 x 및 y몫을 계산합니다.
정수 나누기:
int operator /(int x, int y); uint operator /(uint x, uint y); long operator /(long x, long y); ulong operator /(ulong x, ulong y);오른쪽 피연산자의 값이 0으로 설정되면
System.DivideByZeroException예외가 던져집니다.나누기는 결과를 0에 가까운 방향으로 반올림합니다. 따라서 결과의 절대값은 두 피연산자의 몫 절대값보다 작거나 같은 가능한 가장 큰 정수입니다. 두 피연산자의 부호가 같으면 결과는 0 또는 양수이고, 두 피연산자의 부호가 다르면 결과는 0 또는 음수입니다.
왼쪽 피연산자가 가장 작은 표현 가능한
int또는long값이고 오른쪽 피연산자가–1이면, 오버플로가 발생합니다.checked컨텍스트에서는System.ArithmeticException(또는 해당 서브클래스)가 throw됩니다.unchecked컨텍스트에서는System.ArithmeticException(또는 해당 서브클래스)이 throw되거나 오버플로가 왼쪽 피연산자의 값으로 처리되며 보고되지 않는지 여부는 구현에 따라 정의됩니다.부동 소수점 나누기:
float operator /(float x, float y); double operator /(double x, double y);몫은 IEC 60559 산술 규칙에 따라 계산됩니다. 다음 표에서는 0이 아닌 유한 값, 0, 무한대 및 NaN의 가능한 모든 조합의 결과를 나열합니다. 테이블에서
x및y양수 유한 값입니다.z은x / y의 결과로, 가장 가까운 표현 가능한 값으로 반올림된 것입니다.+y-y+0-0+∞-∞NaN+x+z-z+∞-∞+0-0NaN-x-z+z-∞+∞-0+0NaN+0+0-0NaNNaN+0-0NaN-0-0+0NaNNaN-0+0NaN+∞+∞-∞+∞-∞NaNNaNNaN-∞-∞+∞-∞+∞NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN10진수 나누기:
decimal operator /(decimal x, decimal y);오른쪽 피연산자의 값이 0으로 설정되면
System.DivideByZeroException예외가 던져집니다. 결과값의 크기가 너무 커서 10진수 형식으로 표현할 수 없으면System.OverflowException오류가 발생합니다. 반올림으로 인해 첫 번째 피연산자가 0이 아님에도 불구하고 결과가 0일 수 있습니다. 결과 배율(반올림 전)은 정확한 결과와 동일한 결과를 유지하는 기본 눈금과 가장 가까운 배율입니다. 기본적으로 선호하는 크기는x크기에서y크기를 뺀 것입니다.10진수 나누기는
System.Decimal형식의 나누기 연산자를 사용하는 것과 같습니다.
승격된 (§12.4.8) 형태의 미리 정의된 비승격된 나누기 연산자는 위에 정의된 대로 미리 정의되어 있습니다.
12.12.4 나머지 연산자
양식 x % y연산의 경우 이진 연산자 오버로드 확인(§12.4.5)이 적용되어 특정 연산자 구현을 선택합니다. 피연산자는 선택한 연산자의 매개 변수 형식으로 변환되고 결과 형식은 연산자의 반환 형식입니다.
미리 정의된 나머지 연산자는 아래에 나열되어 있습니다. 연산자들은 모두 x를 y로 나눈 나머지를 계산합니다.
정수의 나머지:
int operator %(int x, int y); uint operator %(uint x, uint y); long operator %(long x, long y); ulong operator %(ulong x, ulong y);x % y의 결과는x – (x / y) * y에 의해 생성된 값입니다.y0이면System.DivideByZeroExceptionthrow됩니다.왼쪽 피연산자가 가장 작은
int또는long값인 경우, 오른쪽 피연산자가–1인 경우에만System.OverflowException가 예외를 throw할 때x / y도 throw됩니다.부동 소수점 나머지:
float operator %(float x, float y); double operator %(double x, double y);다음 표에서는 0이 아닌 유한 값, 0, 무한대 및 NaN의 가능한 모든 조합의 결과를 나열합니다. 테이블에서
x및y양수 유한 값입니다.z는x % y의 결과이며,x – n * y로 계산됩니다. 여기서 n은x / y보다 작거나 같은 가능한 가장 큰 정수입니다. 이 메서드를 사용하여 나머지를 계산하는 방법은 정수 피연산자에 사용되는 방법과 유사하지만, IEC 60559 정의와는 다릅니다(이 정의에서n은x / y에 가장 가까운 정수입니다).+y-y+0-0+∞-∞NaN+x+z+zNaNNaN+x+xNaN-x-z-zNaNNaN-x-xNaN+0+0+0NaNNaN+0+0NaN-0-0-0NaNNaN-0-0NaN+∞NaNNaNNaNNaNNaNNaNNaN-∞NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN10진수 나머지:
decimal operator %(decimal x, decimal y);오른쪽 피연산자의 값이 0으로 설정되면
System.DivideByZeroException예외가 던져집니다.System.ArithmeticException(또는 그 서브클래스)이 throw될 때의 동작은 구현에 따라 정의됩니다.x % y이 예외를 던지지 않는 경우, 준수하는 구현에서는x / y도 예외를 던지지 않아야 합니다. 반올림하기 전의 결과의 배율은 두 피연산자의 배율 중 더 큰 값이며, 결과의 부호가 0이 아닌 경우x의 부호와 같습니다.소수점 나머지는
System.Decimal형식의 나머지 연산자를 사용하는 것과 동일합니다.참고: 이러한 규칙은 모든 형식에 대해 결과에 왼쪽 피연산자의 반대 기호가 없도록 합니다. 끝 메모
위에서 정의된 리프트되지 않은 미리 정의된 나머지 연산자의 리프트 형식(§12.4.8)도 미리 정의됩니다.
12.12.5 더하기 연산자
양식 x + y연산의 경우 이진 연산자 오버로드 확인(§12.4.5)이 적용되어 특정 연산자 구현을 선택합니다. 피연산자는 선택한 연산자의 매개 변수 형식으로 변환되고 결과 형식은 연산자의 반환 형식입니다.
미리 정의된 더하기 연산자는 아래에 나열되어 있습니다. 숫자 및 열거형 형식의 경우 미리 정의된 더하기 연산자는 두 피연산자의 합계를 계산합니다. 하나 또는 두 피연산자가 string형식인 경우 미리 정의된 더하기 연산자는 피연산자의 문자열 표현을 연결합니다.
정수 추가:
int operator +(int x, int y); uint operator +(uint x, uint y); long operator +(long x, long y); ulong operator +(ulong x, ulong y);checked컨텍스트에서는 합계가 결과형의 범위를 벗어나면System.OverflowException이 던져집니다.unchecked컨텍스트에서는 오버플로가 보고되지 않으며 결과 형식 범위를 벗어난 중요한 상위 비트는 삭제됩니다.부동 소수점 추가:
float operator +(float x, float y); double operator +(double x, double y);합계는 IEC 60559 산술 규칙에 따라 계산됩니다. 다음 표에서는 0이 아닌 유한 값, 0, 무한대 및 NaN의 가능한 모든 조합의 결과를 나열합니다. 테이블에서
x와y은 0이 아닌 유한 값이며,z는x + y의 결과입니다.x과y의 크기가 같고 부호가 반대라면,z는 양의 0입니다. 만약에x + y가 너무 커서 대상 형식으로 나타낼 수 없는 경우,z은x + y와 같은 부호를 가진 무한대입니다.y+0-0+∞-∞NaNxzxx+∞-∞NaN+0y+0+0+∞–∞NaN-0y+0-0+∞-∞NaN+∞+∞+∞+∞+∞NaNNaN-∞-∞-∞-∞NaN-∞NaNNaNNaNNaNNaNNaNNaNNaN10진수 추가:
decimal operator +(decimal x, decimal y);결과값의 크기가 너무 커서 10진수 형식으로 표현할 수 없으면
System.OverflowException오류가 발생합니다. 반올림하기 전에 결과의 배율은 두 피연산자의 배율보다 큽니다.10진수 더하기는
System.Decimal형식의 더하기 연산자를 사용하는 것과 같습니다.열거 추가. 모든 열거형 형식은 다음과 같은 미리 정의된 연산자를 암시적으로 제공합니다. 여기서
E열거형 형식이고UE기본 형식입니다.E operator +(E x, U y); E operator +(U x, E y);런타임에 이러한 연산자는
(E)((U)x + (U)y)과 정확히 동일하게 평가됩니다.문자열 연결:
string operator +(string x, string y); string operator +(string x, object y); string operator +(object x, string y);이진
+연산자의 이러한 오버로드는 문자열 연결을 수행합니다. 피연산자가null인 경우, 빈 문자열로 대체됩니다. 그렇지 않으면string이외의 피연산자는ToString형식에서 상속된 가상object메서드를 호출하여 문자열 표현으로 변환됩니다.ToStringnull반환하면 빈 문자열이 대체됩니다.예제:
class Test { static void Main() { string s = null; Console.WriteLine("s = >" + s + "<"); // Displays s = >< int i = 1; Console.WriteLine("i = " + i); // Displays i = 1 float f = 1.2300E+15F; Console.WriteLine("f = " + f); // Displays f = 1.23E+15 decimal d = 2.900m; Console.WriteLine("d = " + d); // Displays d = 2.900 } }주석에 표시된 출력은 US-English 시스템의 일반적인 결과입니다. 정확한 출력은 실행 환경의 지역 설정에 따라 달라질 수 있습니다. 문자열 연결 연산자 자체는 각 경우에 동일한 방식으로 동작하지만 실행 중에 암시적으로 호출되는
ToString메서드는 지역 설정의 영향을 받을 수 있습니다.예제 종료
문자열 연결 연산자의 결과는 왼쪽 피연산자의 문자와 오른쪽 피연산자의 문자로 구성된
string입니다. 문자열 연결 연산자는null값을 반환하지 않습니다. 결과 문자열을 할당하는 데 사용할 수 있는 메모리가 충분하지 않은 경우System.OutOfMemoryExceptionthrow될 수 있습니다.대리자 조합입니다. 모든 대리자 형식은 다음과 같은 미리 정의된 연산자를 암시적으로 제공합니다. 여기서
D대리자 형식입니다.D operator +(D x, D y);첫 번째 피연산자가
null이면 연산 결과는 두 번째 피연산자의 값입니다(비록 그 값이null일지라도). 그렇지 않고 두 번째 피연산자가null인 경우, 그러면 연산 결과는 첫 번째 피연산자의 값입니다. 그렇지 않으면, 연산의 결과로 첫 번째 피연산자의 호출 목록에 있는 요소가 먼저 나오고 이어서 두 번째 피연산자의 호출 목록에 있는 요소들이 포함된 새 대리자 인스턴스가 생성됩니다. 즉, 결과 대리자의 호출 목록은 두 피연산자의 호출 목록을 이어붙인 것입니다.참고: 대리자 조합의 예는 §12.12.6 및 §21.6을 참조하세요.
System.Delegate대리자 형식이 아니므로 연산자 +는 대리자 형식에 대해 정의되지 않습니다. 끝 노트
승격된 (§12.4.8) 형태의 비승격된 미리 정의된 추가 연산자도 미리 정의되어 있습니다.
12.12.6 빼기 연산자
양식 x – y연산의 경우 이진 연산자 오버로드 확인(§12.4.5)이 적용되어 특정 연산자 구현을 선택합니다. 피연산자는 선택한 연산자의 매개 변수 형식으로 변환되고 결과 형식은 연산자의 반환 형식입니다.
미리 정의된 빼기 연산자는 아래에 나열되어 있습니다. 연산자는 모두 y에서 x를 뺍니다.
정수 빼기:
int operator –(int x, int y); uint operator –(uint x, uint y); long operator –(long x, long y); ulong operator –(ulong x, ulong ychecked컨텍스트에서 차이가 결과 형식의 범위를 벗어나면System.OverflowException이 던져집니다.unchecked컨텍스트에서는 오버플로가 보고되지 않으며 결과 형식 범위를 벗어난 중요한 상위 비트는 삭제됩니다.부동 소수점 빼기
float operator –(float x, float y); double operator –(double x, double y);차이는 IEC 60559 산술의 규칙에 따라 계산됩니다. 다음 표에서는 0이 아닌 유한 값, 0, 무한대 및 NaN의 가능한 모든 조합의 결과를 나열합니다. 테이블에서
x와y은 0이 아닌 유한 값이며,z는x – y의 결과입니다.x및y같으면z양의 0입니다. 만약에x – y가 너무 커서 대상 형식으로 나타낼 수 없는 경우,z은x – y와 같은 부호를 가진 무한대입니다.y+0-0+∞-∞NaNxzxx-∞+∞NaN+0-y+0+0-∞+∞NaN-0-y-0+0-∞+∞NaN+∞+∞+∞+∞NaN+∞NaN-∞-∞-∞-∞-∞NaNNaNNaNNaNNaNNaNNaNNaNNaN위의 표에서
-y항목은 값이 음수가 아니라 의y를 뜻합니다.10진수 빼기:
decimal operator –(decimal x, decimal y);결과값의 크기가 너무 커서 10진수 형식으로 표현할 수 없으면
System.OverflowException오류가 발생합니다. 반올림하기 전에 결과의 배율은 두 피연산자의 배율보다 큽니다.10진수 빼기는
System.Decimal형식의 빼기 연산자를 사용하는 것과 같습니다.열거형 빼기 모든 열거형 형식은 다음과 같은 미리 정의된 연산자를 암시적으로 제공합니다. 여기서
E열거형 형식이고UE기본 형식입니다.U operator –(E x, E y);이 연산자는
(U)((U)x – (U)y)와 정확히 동일하게 평가됩니다. 즉, 연산자는x서수 값과y간의 차이를 계산하고 결과의 형식은 열거형의 기본 형식입니다.E operator –(E x, U y);이 연산자는
(E)((U)x – y)와 정확히 동일하게 평가됩니다. 즉, 연산자는 열거형의 기본 형식에서 값을 빼 열거형의 값을 생성합니다.위임자 제거. 모든 대리자 형식은 다음과 같은 미리 정의된 연산자를 암시적으로 제공합니다. 여기서
D대리자 형식입니다.D operator –(D x, D y);의미 체계는 다음과 같습니다.
- 첫 번째 피연산자가
null이라면, 연산의 결과는null입니다. - 그렇지 않고 두 번째 피연산자가
null인 경우, 그러면 연산 결과는 첫 번째 피연산자의 값입니다. - 그렇지 않으면 두 피연산자는 비어있지 않은 호출 목록(§21.2)을 나타냅니다.
- 대리자 같음 연산자(§12.14.9)에 의해 결정된 대로 목록이 같으면 연산
null결과가 됩니다. - 그렇지 않으면 두 번째 피연산자의 목록이 첫 번째 피연산자의 하위 목록인 경우 작업 결과는 두 번째 피연산자의 항목이 제거된 첫 번째 피연산자의 목록으로 구성된 새 호출 목록입니다. 하위 목록의 동등성을 확인하기 위해 대리자 동등 연산자와 마찬가지로 각 항목이 비교됩니다. 두 번째 피연산자의 목록이 첫 번째 피연산자 목록에서 여러 연속 항목의 하위 목록과 일치하는 경우, 마지막으로 일치하는 연속된 하위 목록이 제거됩니다.
- 그렇지 않으면 작업의 결과는 왼쪽에 위치한 피연산자의 값입니다.
- 대리자 같음 연산자(§12.14.9)에 의해 결정된 대로 목록이 같으면 연산
피연산자의 목록(있는 경우)은 프로세스에서 변경되지 않습니다.
예제:
delegate void D(int x); class C { public static void M1(int i) { ... } public static void M2(int i) { ... } } class Test { static void Main() { D cd1 = new D(C.M1); D cd2 = new D(C.M2); D list = null; list = null - cd1; // null list = (cd1 + cd2 + cd2 + cd1) - null; // M1 + M2 + M2 + M1 list = (cd1 + cd2 + cd2 + cd1) - cd1; // M1 + M2 + M2 list = (cd1 + cd2 + cd2 + cd1) - (cd1 + cd2); // M2 + M1 list = (cd1 + cd2 + cd2 + cd1) - (cd2 + cd2); // M1 + M1 list = (cd1 + cd2 + cd2 + cd1) - (cd2 + cd1); // M1 + M2 list = (cd1 + cd2 + cd2 + cd1) - (cd1 + cd1); // M1 + M2 + M2 + M1 list = (cd1 + cd2 + cd2 + cd1) - (cd1 + cd2 + cd2 + cd1); // null } }예제 종료
- 첫 번째 피연산자가
리프트된 (§12.4.8) 형태의 미리 정의된 빼기 연산자들은 위에서 정의한 비리프트 형태와 함께 미리 정의되어 있습니다.
12.13 Shift 연산자
<< 및 >> 연산자는 비트 시프트 작업을 수행하는 데 사용됩니다.
shift_expression
: additive_expression
| shift_expression '<<' additive_expression
| shift_expression right_shift additive_expression
;
shift_expression 피연산자의 컴파일 시간 형식이 dynamic인 경우 식이 동적으로 바인딩됩니다(§12.3.3). 이 경우 식의 컴파일 시간 타입은 dynamic이며, 아래에 설명된 해상도는 컴파일 시간 타입이 dynamic인 피연산자의 런타임 타입을 사용하여 런타임에 처리됩니다.
양식 x << count 또는 x >> count연산의 경우 이진 연산자 오버로드 확인(§12.4.5)이 적용되어 특정 연산자 구현을 선택합니다. 피연산자는 선택한 연산자의 매개 변수 형식으로 변환되고 결과 형식은 연산자의 반환 형식입니다.
오버로드된 시프트 연산자를 선언할 때 첫 번째 피연산자의 형식은 항상 연산자 선언을 포함하는 클래스 또는 구조체여야 하며 두 번째 피연산자의 형식은 항상 int합니다.
미리 정의된 시프트 연산자는 다음과 같습니다.
왼쪽으로 이동:
int operator <<(int x, int count); uint operator <<(uint x, int count); long operator <<(long x, int count); ulong operator <<(ulong x, int count);<<연산자는 아래에 설명된 방식으로 계산된 비트 수만큼x을 왼쪽으로 이동시킵니다.결과 형식의
x범위를 벗어난 상위 비트는 삭제되고, 나머지 비트는 왼쪽으로 이동하고, 낮은 순서의 빈 비트 위치는 0으로 설정됩니다.오른쪽으로 이동하기:
int operator >>(int x, int count); uint operator >>(uint x, int count); long operator >>(long x, int count); ulong operator >>(ulong x, int count);>>연산자는 아래 설명된 대로 계산된 비트 수만큼x오른쪽으로 이동합니다.xint또는long형식이면 낮은 순서의x비트가 삭제되고, 나머지 비트는 오른쪽으로 이동하고,x음수가 아닌 경우 상위 빈 비트 위치는 0으로 설정되고x음수이면 1로 설정됩니다.xuint또는ulong형식이면 낮은 순서의x비트가 삭제되고, 나머지 비트가 오른쪽으로 이동되고, 상위 빈 비트 위치가 0으로 설정됩니다.
미리 정의된 연산자의 경우 이동할 비트 수는 다음과 같이 계산됩니다.
-
x의 유형이int또는uint일 때, 시프트 수는count의 하위 5비트에 의해 결정됩니다. 즉, 시프트 수는count & 0x1F에서 계산됩니다. -
x의 유형이long또는ulong일 때, 시프트 수는count의 하위 6비트에 의해 결정됩니다. 즉, 시프트 수는count & 0x3F에서 계산됩니다.
결과 시프트 수가 0이면 시프트 연산자는 단순히 x값을 반환합니다.
Shift 연산은 오버플로를 유발하지 않으며 확인된 컨텍스트와 선택되지 않은 컨텍스트에서 동일한 결과를 생성합니다.
>> 연산자의 왼쪽 피연산자가 부호 있는 정수 계열 형식인 경우 연산자는 피연산자의 가장 중요한 비트(부호 비트)의 값이 높은 순서의 빈 비트 위치로 전파되는 산술 시프트를 오른쪽으로 수행합니다.
>> 연산자의 왼쪽 피연산자가 부호 없는 정수 형식일 경우, 연산자는 상위 비트에서 비어 있는 위치가 항상 0으로 설정되는 논리적 오른쪽 시프트를 수행합니다. 피연산자 형식에서 유추된 것과 반대 연산을 수행하려면 명시적 캐스트를 사용할 수 있습니다.
예제: 만약
x가int형식의 변수라면, 작업unchecked ((int)((uint)x >> y))는x의 논리적 오른쪽 시프트를 수행합니다. 예제 종료
위에서 정의한 리프티드(§12.4.8) 형태의 사전 정의된 시프트 연산자들은 해제되지 않은 형태의 사전 정의된 시프트 연산자도 포함됩니다.
12.14 관계형 및 형식 테스트 연산자
12.14.1 일반
==, !=, <, >, <=, >=, is및 as 연산자를 관계형 및 형식 테스트 연산자라고 합니다.
relational_expression
: shift_expression
| relational_expression '<' shift_expression
| relational_expression '>' shift_expression
| relational_expression '<=' shift_expression
| relational_expression '>=' shift_expression
| relational_expression 'is' type
| relational_expression 'is' pattern
| relational_expression 'as' type
;
equality_expression
: relational_expression
| equality_expression '==' relational_expression
| equality_expression '!=' relational_expression
;
참고:
is연산자의 오른쪽 피연산자를 조회하려면 먼저 형식으로 테스트하고, 그런 다음 여러 토큰에 걸쳐 있을 수 있는 식으로 테스트해야 합니다. 피연산자가 식일 경우, 패턴 식의 우선 순위는 shift_expression와 같거나 더 높아야 합니다. 끝 메모
참고: 오른쪽에 있는 형식과
relational_expressionis사이에 문법 모호성이 있습니다. 한정된 식별자의 유효한 구문 분석일 수 있습니다. 이러한 경우 이전 언어 버전과의 호환성을 위해 형식으로 바인딩하지 못하는 경우에만 첫 번째 항목(상수 또는 형식이어야 합니다)으로 확인됩니다. 이러한 모호성은 이러한 식의 오른쪽에만 있습니다.
연산자는 is§12.14.12as 에서 설명하고 연산자는 §12.14.13에 설명되어 있습니다.
==, !=, <, >, <= 및 >= 연산자는 비교 연산자입니다.
default_literal(§12.8.21)가 <, >, <=또는 >= 연산자의 피연산자로 사용되는 경우 컴파일 시간 오류가 발생합니다.
기본 리터럴이 == 또는 != 연산자의 피연산자로 사용될 경우 컴파일 시 오류가 발생합니다.
default_literal이 is 또는 as 연산자의 왼쪽 피연산자로 사용될 경우 컴파일 시간 오류가 발생합니다.
비교 연산자의 피연산자에 컴파일 시간 형식이 dynamic경우 식이 동적으로 바인딩됩니다(§12.3.3). 이 경우, 식의 컴파일 시간 형식은 dynamic이고, 아래에 설명된 해상도는 컴파일 시간 형식이 dynamic인 해당 피연산자의 런타임 형식을 사용하여 런타임에 수행됩니다.
«op»가 비교 연산자인 x «op» y양식의 작업의 경우 특정 연산자 구현을 선택하기 위해 오버로드 확인(§12.4.5)이 적용됩니다. 피연산자는 선택한 연산자의 매개 변수 형식으로 변환되고 결과 형식은 연산자의 반환 형식입니다.
equality_expression 두 피연산자가 모두 null 리터럴인 경우, 오버로드 해석이 수행되지 않으며, 연산자가 true인지 false인지에 따라, 식은 == 또는 !=의 상수 값으로 평가됩니다.
미리 정의된 비교 연산자는 다음 하위클래스에 설명되어 있습니다. 미리 정의된 모든 비교 연산자는 다음 표에 설명된 대로 부울 형식의 결과를 반환합니다.
| 수술 | 결과 |
|---|---|
x == y |
만약 true이 x와 같으면 y, 그렇지 않으면 false. |
x != y |
true 이 x와 같지 않으면 y이고, 그렇지 않으면 false. |
x < y |
true
x미만이면 y, 그렇지 않으면 false. |
x > y |
true이 x보다 크면 y, 그렇지 않으면 false. |
x <= y |
true이 x보다 작거나 같으면 y, 그렇지 않으면 false |
x >= y |
true이 x보다 크거나 같은 경우 y, 그렇지 않으면 false |
12.14.2 정수 비교 연산자
미리 정의된 정수 비교 연산자는 다음과 같습니다.
bool operator ==(int x, int y);
bool operator ==(uint x, uint y);
bool operator ==(long x, long y);
bool operator ==(ulong x, ulong y);
bool operator !=(int x, int y);
bool operator !=(uint x, uint y);
bool operator !=(long x, long y);
bool operator !=(ulong x, ulong y);
bool operator <(int x, int y);
bool operator <(uint x, uint y);
bool operator <(long x, long y);
bool operator <(ulong x, ulong y);
bool operator >(int x, int y);
bool operator >(uint x, uint y);
bool operator >(long x, long y);
bool operator >(ulong x, ulong y);
bool operator <=(int x, int y);
bool operator <=(uint x, uint y);
bool operator <=(long x, long y);
bool operator <=(ulong x, ulong y);
bool operator >=(int x, int y);
bool operator >=(uint x, uint y);
bool operator >=(long x, long y);
bool operator >=(ulong x, ulong y);
이러한 각 연산자는 두 정수 피연산자의 숫자 값을 비교하고 특정 관계가 bool 또는 true여부를 나타내는 false 값을 반환합니다.
정의되지 않은 정수 비교 연산자의 리프트된 버전(§12.4.8)도 미리 정의되어 있습니다.
12.14.3 부동 소수점 비교 연산자
미리 정의된 부동 소수점 비교 연산자는 다음과 같습니다.
bool operator ==(float x, float y);
bool operator ==(double x, double y);
bool operator !=(float x, float y);
bool operator !=(double x, double y);
bool operator <(float x, float y);
bool operator <(double x, double y);
bool operator >(float x, float y);
bool operator >(double x, double y);
bool operator <=(float x, float y);
bool operator <=(double x, double y);
bool operator >=(float x, float y);
bool operator >=(double x, double y);
연산자는 IEC 60559 표준의 규칙에 따라 피연산자를 비교합니다.
피연산자 중 하나가 NaN이면 false를 제외한 모든 연산자의 결과는 !=이며, false의 결과는 입니다. 어떠한 두 피연산자에 대해서도 x != y는 항상 !(x == y)과 동일한 결과를 산출합니다. 그러나 하나 또는 두 피연산자가 NaN인 경우 <, >, <=및 >= 연산자는 반대 연산자의 논리적 부정과 동일한 결과를 생성하지 않습니다.
예제:
x및y중 하나가 NaN인 경우x < y는false이지만,!(x >= y)은true이다. 예제 종료
두 피연산자가 모두 NaN이 아닌 경우 연산자는 두 부동 소수점 피연산자의 값을 순서와 비교합니다.
–∞ < –max < ... < –min < –0.0 == +0.0 < +min < ... < +max < +∞
여기서 min 및 max 지정된 부동 소수점 형식으로 나타낼 수 있는 가장 작고 가장 큰 양의 유한 값입니다. 이 순서 지정의 주목할 만한 효과는 다음과 같습니다.
- 음수 및 양수 0은 같은 것으로 간주됩니다.
- 음의 무한대는 다른 모든 값보다 작지만 다른 음의 무한대와 같은 것으로 간주됩니다.
- 양수 무한대는 다른 모든 값보다 크지만 다른 양수 무한대와 같은 것으로 간주됩니다.
위에서 정의한 미리 정의된 리프팅되지 않은 부동 소수점 비교 연산자의 리프트(§12.4.8) 형식도 미리 정의됩니다.
12.14.4 10진수 비교 연산자
미리 정의된 10진수 비교 연산자는 다음과 같습니다.
bool operator ==(decimal x, decimal y);
bool operator !=(decimal x, decimal y);
bool operator <(decimal x, decimal y);
bool operator >(decimal x, decimal y);
bool operator <=(decimal x, decimal y);
bool operator >=(decimal x, decimal y);
이러한 각 연산자는 두 소수 피연산자의 숫자 값을 비교하고 특정 관계가 bool 또는 true여부를 나타내는 false 값을 반환합니다. 각 10진수 비교는 System.Decimal형식의 해당 관계형 또는 같음 연산자를 사용하는 것과 같습니다.
리프팅된(§12.4.8) 형태의 미리 정의된 10진수 비교 연산자도 미리 정의되어 있습니다.
12.14.5 부울 같음 연산자
정의된 부울 같음 연산자는 다음과 같습니다.
bool operator ==(bool x, bool y);
bool operator !=(bool x, bool y);
둘 다 == 및 true가 x이거나 둘 다 y 및 true가 x인 경우 y의 결과는 false입니다. 그렇지 않으면 결과가 false.
둘 다 != 및 false가 x이거나 둘 다 y 및 true가 x인 경우 y의 결과는 false입니다. 그렇지 않으면 결과가 true. 피연산자가 bool형식이면 != 연산자는 ^ 연산자로 동일한 결과를 생성합니다.
리프팅된(§12.4.8) 형태의 위에서 정의한 미리 정의된 부울 같음 연산자는 또한 미리 정의되어 있습니다.
12.14.6 열거형 비교 연산자
모든 열거형 형식은 다음과 같은 미리 정의된 비교 연산자를 암시적으로 제공합니다.
bool operator ==(E x, E y);
bool operator !=(E x, E y);
bool operator <(E x, E y);
bool operator >(E x, E y);
bool operator <=(E x, E y);
bool operator >=(E x, E y);
x와 y가 기본 유형 x «op» y의 E 열거형 형식인 식이며, «op»가 비교 연산자 중 하나일 때, U평가의 결과는 ((U)x) «op» ((U)y)평가와 정확히 동일합니다. 즉, 열거형 형식 비교 연산자는 두 피연산자의 기본 정수 값을 비교하기만 하면 됩니다.
확장된(§12.4.8) 상위의 미리 정의된 열거형 비교 연산자의 형식도 미리 정의되어 있습니다.
12.14.7 참조 형식 같음 연산자
C 모든 클래스 형식은 다음과 같은 미리 정의된 참조 형식 같음 연산자를 암시적으로 제공합니다.
bool operator ==(C x, C y);
bool operator !=(C x, C y);
C에 대해 미리 정의된 같음 연산자가 존재하지 않는 한 (예: C이 string이거나 System.Delegate일 때)
연산자는 두 참조를 같음 또는 비같이 비교한 결과를 반환합니다.
operator ==은 true 및 x이 동일한 인스턴스를 참조하거나 둘 다 y일 경우에만 null을 반환하며, operator !=는 동일한 피연산자를 가진 경우에만 true이 operator ==을 반환한다면 false을 반환합니다.
일반 적용 가능성 규칙(§12.6.4.2) 외에도 미리 정의된 참조 형식 같음 연산자를 적용하려면 다음 중 하나가 필요합니다.
- 두 피연산자는 모두 reference_type 또는 리터럴
null로 인식되는 형식의 값입니다. 또한 ID 또는 명시적 참조 변환(§10.3.5)이 한 피연산자에서 다른 피연산자의 형식으로의 변환이 가능합니다. - 한 피연산자는 리터럴
null이며, 다른 피연산자는 값 형식이 아닌 것으로 알려져 있고, 값 형식 제약 조건이 없는TT형식의 값입니다.
이러한 조건 중 하나가 true가 아니면 바인딩 시간 오류가 발생합니다.
참고: 이러한 규칙의 주목할 만한 의미는 다음과 같습니다.
- 바인딩 시 서로 다른 것으로 알려진 두 참조를 비교하기 위해 미리 정의된 참조 형식 같음 연산자를 사용하는 것은 바인딩 시간 오류입니다. 예를 들어 피연산자의 바인딩 시간 형식이 두 클래스 형식이고 둘 다 다른 형식에서 파생되지 않는 경우 두 피연산자가 동일한 개체를 참조하는 것은 불가능합니다. 따라서 작업은 바인딩 시간 오류로 간주됩니다.
- 미리 정의된 참조 형식 동등 연산자는 값 형식 피연산자를 비교할 수 없습니다(단, 형식 매개 변수가
null과 비교되는 경우는 특별히 처리됩니다).- 미리 정의된 참조 형식의 같음 연산자의 피연산자는 박싱되지 않습니다. 새로 할당된 '박싱된' 인스턴스에 대한 참조가 다른 모든 참조와 반드시 다르기 때문에 이러한 박싱 작업을 수행하는 것은 무의미합니다.
양식
x == y또는x != y연산의 경우 적용 가능한 사용자 정의operator ==또는operator !=이 있는 경우, 연산자 오버로드 결의 규칙(§12.4.5)은 미리 정의된 참조 타입 동등 연산자 대신 그 연산자를 선택합니다. 피연산자 중 하나 또는 둘 다를 형식object으로 명시적으로 캐스팅하여 미리 정의된 참조 형식 등치 연산자를 항상 선택할 수 있습니다.끝 메모
예제: 다음 예제에서는 제약이 없는 형식 매개 변수 형식의 인수가
null여부를 확인합니다.class C<T> { void F(T x) { if (x == null) { throw new ArgumentNullException(); } ... } }
x == null구문은T이 Null 허용 안하는 값 형식을 나타낼 수 있음에도 불구하고 허용되며,false이 Null 허용 안하는 값 형식인 경우 결과는 단순히T로 정의됩니다.예제 종료
연산의 양식이 x == y 또는 x != y인 경우, 적용 가능한 operator == 또는 operator !=이 있으면, 연산자 오버로드 해석(§12.4.5) 규칙은 미리 정의된 참조 타입 같음 연산자 대신 그 연산자를 선택합니다.
참고: 두 피연산자를 형식
object로 명시적으로 캐스팅함으로써, 미리 정의된 참조 타입 동등성 연산자를 선택할 수 있습니다. 끝 메모
예제: 예제
class Test { static void Main() { string s = "Test"; string t = string.Copy(s); Console.WriteLine(s == t); Console.WriteLine((object)s == t); Console.WriteLine(s == (object)t); Console.WriteLine((object)s == (object)t); } }출력을 생성합니다.
True False False False
s및t변수는 동일한 문자를 포함하는 두 개의 고유 문자열 인스턴스를 참조합니다. 두 피연산자가True모두 형식일 때 미리 정의된 문자열 같음 연산자(string)가 선택되기 때문에 첫 번째 비교 출력입니다.False형식의operator ==오버로드는 어느 한 연산자가 바인딩 시간 형식string을 가질 때 적용되지 않으므로, 나머지 비교는 모두object을 출력합니다.위의 기술은 값 형식에 의미가 없습니다. 예제
class Test { static void Main() { int i = 123; int j = 123; Console.WriteLine((object)i == (object)j); } }출력은
False입니다. 캐스트 작업이 박싱된int값의 두 개의 별도 인스턴스에 대한 참조를 생성하기 때문입니다.예제 종료
12.14.8 문자열 같음 연산자
미리 정의된 문자열 같음 연산자는 다음과 같습니다.
bool operator ==(string x, string y);
bool operator !=(string x, string y);
다음 중 하나가 true이면 두 string 값이 같은 것으로 간주됩니다.
- 두 값 모두
null. - 두 값 모두 길이와 각 문자 위치가 동일한 문자열 인스턴스에 대한
null이 없는 참조입니다.
문자열 같음 연산자는 문자열 참조가 아닌 문자열 값을 비교합니다. 두 개의 별도 문자열 인스턴스에 정확히 동일한 문자 시퀀스가 포함된 경우 문자열 값은 같지만 참조는 다릅니다.
참고: §12.14.7에 설명된 대로 참조 형식 같음 연산자를 사용하여 문자열 값 대신 문자열 참조를 비교할 수 있습니다. 끝 메모
12.14.9 같음 연산자 대리자
미리 정의된 대리자 같음 연산자는 다음과 같습니다.
bool operator ==(System.Delegate x, System.Delegate y);
bool operator !=(System.Delegate x, System.Delegate y);
두 대리자 인스턴스는 다음과 같이 동일하게 간주됩니다.
- 대리자 인스턴스 중 하나가
null인 경우, 두 인스턴스 모두null인 경우에만 서로 동일합니다. - 대리자들의 런타임 형식이 다르면 결코 같지 않습니다.
- 두 대리자 인스턴스에 모두 호출 목록(§21.2)이 있는 경우 해당 인스턴스는 해당 호출 목록이 길이가 같고 한 호출 목록의 각 항목이 다른 호출 목록에서 순서대로 해당 항목과 같을 때만 동일합니다.
다음 규칙들은 호출 목록 항목의 동등성을 지배합니다.
- 두 호출 목록 항목이 모두 동일한 정적 메서드를 참조하는 경우 항목은 같습니다.
- 두 호출 목록 항목이 모두 동일한 대상 개체(참조 같음 연산자로 정의됨)에서 동일한 비정적 메서드를 참조하는 경우 항목은 동일합니다.
- 캡처된 외부 변수 인스턴스의 동일한(비어 있을 수 있음) 집합이 있는 의미상 동일한 익명 함수(§12.21)의 평가에서 생성된 호출 목록 항목은 같을 수 있습니다(필수는 아님).
연산자 오버로드 확인이 대리자 같음 연산자로 확인되고 두 피연산자의 바인딩 시간 형식이 §21 에 System.Delegate설명된 대로 대리자 형식이고 바인딩 형식 피연산자 형식 간에 ID 변환이 없으면 바인딩 시간 오류가 발생합니다.
참고: 이 규칙은 서로 다른 유형의 대리자 인스턴스에 대한 참조이기 때문에 비
null값을 동일하게 간주할 수 없는 비교를 방지합니다. 끝 메모
12.14.10 nullable 값 형식과 null 리터럴 간의 같음 연산자
== 및 != 연산자는 연산에 대해 미리 정의되거나 사용자 정의 연산자(해제 또는 해제된 형식)가 없더라도 한 피연산자가 nullable 값 형식의 값이 되고 다른 피연산자는 null 리터럴이 되도록 허용합니다.
양식 중 하나를 사용하는 작업
x == null null == x x != null null != x
여기서 x가 nullable 값 형식의 표현식인 경우, 연산자 오버로드 확인(§12.4.5)이 적용 가능한 연산자를 찾지 못하면, 결과는 대신 HasValue의 x 속성에서 계산됩니다. 특히 처음 두 양식은 !x.HasValue변환되고 마지막 두 양식은 x.HasValue변환됩니다.
12.14.11 튜플 같음 연산자
튜플 동등 연산자는 사전 순서로 튜플 피연산자의 요소에 쌍씩 적용됩니다.
연산자
피연산자 e 튜플로 분류되는 경우 e1...en 요소는 튜플 식의 요소 식을 계산한 결과여야 합니다. 그렇지 않으면, e이 튜플 형식의 값인 경우, 요소는 t.Item1...t.Itemn을 평가한 결과인 te입니다.
튜플 등가 연산자의 피연산자 x와 y은 동일한 아리티를 가져야 하며, 그렇지 않으면 컴파일 시간 오류가 발생합니다.
xi 및 yi각 요소 쌍에 대해 동일한 같음 연산자가 적용되어야 하며, 결과는 bool형식, dynamic형식, bool로 암시적 변환이 가능한 형식, 또는 true 및 false 연산자를 정의하는 형식이 되어야 합니다.
튜플 같음 연산자 x == y 다음과 같이 평가됩니다.
- 왼쪽 피연산자
x가 평가가 이루어집니다. - 오른쪽 피연산자
y가 평가됩니다. - 각 원소 쌍
xi및yi에 대해 사전 순으로:- 연산자
xi == yi가 평가되고, 형식bool의 결과는 다음과 같이 얻어집니다.- 비교 결과가
bool이면, 그것이 결과입니다. - 그렇지 않으면 비교에서
dynamic생성한 경우 연산자false동적으로 호출되고 결과bool값은 논리 부정 연산자(!)로 무효화됩니다. - 그렇지 않으면 비교 형식에
bool대한 암시적 변환이 있는 경우 해당 변환이 적용됩니다. - 그렇지 않으면 비교 형식에 연산자
false있는 경우 해당 연산자가 호출되고 결과bool값이 논리 부정 연산자(!)와 부정됩니다.
- 비교 결과가
- 결과가
boolfalse이면, 더 이상 평가가 이루어지지 않으며 튜플 같음 연산자의 결과는false입니다.
- 연산자
- 모든 요소 비교가
true산출되면 튜플 동등 연산자의 결과가true.
튜플 같음 연산자 x != y 다음과 같이 평가됩니다.
- 왼쪽 피연산자
x가 평가가 이루어집니다. - 오른쪽 피연산자
y가 평가됩니다. - 각 원소 쌍
xi및yi에 대해 사전 순으로:- 연산자
xi != yi가 평가되고, 형식bool의 결과는 다음과 같이 얻어집니다.- 비교 결과가
bool이면, 그것이 결과입니다. - 비교에서
dynamic이 생성되면 연산자true이 동적으로 호출되며, 그 결과로 나오는bool값이 최종 결과입니다. - 그렇지 않으면 비교 형식에
bool대한 암시적 변환이 있는 경우 해당 변환이 적용됩니다. - 그렇지 않은 경우 비교 형식에 연산자
true있으면 해당 연산자가 호출되고 결과bool값이 결과입니다.
- 비교 결과가
- 결과가
booltrue이면, 더 이상 평가가 이루어지지 않으며 튜플 같음 연산자의 결과는true입니다.
- 연산자
- 모든 요소 비교가
false산출되면 튜플 동등 연산자의 결과가false.
12.14.12 is 연산자
is 연산자의 두 가지 형태가 있습니다. 하나는 오른쪽에 형식이 있는 is-type 연산자라는 것입니다. 다른 하나는 오른쪽에 위치한 패턴을 가진 is-pattern 연산자입니다.
12.14.12.1 is-type 연산자
is-type 연산자 사용하여 개체의 런타임 형식이 지정된 형식과 호환되는지 확인합니다. 확인은 런타임에 수행됩니다.
E is T이 식이고 E가 T이외의 형식인 경우에 대한 작업 dynamic의 결과는 E가 null이 아니고 참조 변환, 박싱 변환, 언박싱 변환, 래핑 변환 또는 언래핑 변환을 통해 T 형식으로 성공적으로 변환될 수 있는지를 나타내는 부울 값입니다.
작업은 E is T 다음과 같이 평가됩니다.
-
E익명 함수 또는 메서드 그룹인 경우 컴파일 시간 오류가 발생합니다. - nullable 참조 형식(
T)인 경우 컴파일 시간 오류가 발생합니다. -
E이null리터럴이거나E의 값이null일 경우, 결과는false입니다. - 그렇지 않은 경우:
-
R는E의 런타임 형식입니다. -
D다음과 같이 파생R할 수 있습니다.-
R이 nullable 값 형식인 경우,D은R의 기본 형식입니다. - 그렇지 않으면
D는R입니다.
-
- 결과는 다음과 같이 달라집니다
DT.-
T이 참조 형식인 경우, 결과는true입니다.- ID 변환이
D존재하며T, -
D는 참조 형식이며D에서T로의 암시적 참조 변환이 존재하거나 -
D는 값 형식이며 boxing 변환이D존재합니다T.
- ID 변환이
-
T이 nullable 값 형식인 경우,true이D의 기본 형식이면 결과는T입니다. -
T이 nullable이 아닌 값 형식인 경우,true와D이 같은 형식이면 결과는T입니다. - 그렇지 않으면 결과가
false.
-
-
사용자 정의 변환은 is 연산자가 고려하지 않습니다.
참고: 런타임에
is연산자가 평가되므로 모든 형식 인수가 대체되었으며 열려 있는 형식(§8.4.3)이 없습니다. 끝 메모
참고:
is연산자는 다음과 같이 컴파일 시간 형식 및 변환 측면에서 이해할 수 있습니다. 여기서C은E의 컴파일 시간 형식입니다.
- 컴파일 시간 형식의
e가T와 동일하거나, 컴파일 시간 형식의 에서 로 암시적 참조 변환(§10.2.8), boxing 변환(§10.2.9), 래핑 변환(E), 또는 명시적 래핑 해제 변환(T)이 존재하는 경우:
C이 null이 아닌 값 형식의 경우, 작업의 결과는true입니다.- 그렇지 않으면 연산의 결과는
E != null를 평가하는 것과 같습니다.- 그렇지 않은 경우 명시적 참조 변환(§10.3.5) 또는 unboxing 변환(§10.3.7)
CT이 있는 경우 또는 열려 있는 형식(C)인 경우T위와 같이 런타임 검사를 수행해야 합니다.- 그렇지 않으면
E을(를)T형식으로 변환하기 위한 참조, 박싱(boxing), 래핑 또는 래핑 해제 변환은 불가능하며, 작업 결과는false입니다. 컴파일러는 컴파일 시간 형식에 따라 최적화를 구현할 수 있습니다.끝 메모
12.14.12.2 is 패턴 연산자
is-pattern 연산자을 사용하여 식 으로 계산된 값이 지정된 패턴(§11)과 일치하는지를 확인합니다. 확인은 런타임에 수행됩니다. 값이 패턴과 일치하는 경우 is-pattern 연산자의 결과는 true입니다. 그렇지 않으면 false입니다.
E is P이 E 형식의 관계형 식이고 T이 패턴인 경우 P형태의 표현식에 대해 다음 조건 중 하나가 충족되면 컴파일 시간 오류입니다.
패턴의 모든 single_variable_designation해당 relational_expression 테스트할 때 확실히 할당되는 새 지역 변수(§9.4)를 도입합니다true.
12.14.13 As 연산자
as 연산자는 값을 지정된 참조 형식 또는 nullable 값 형식으로 명시적으로 변환하는 데 사용됩니다. 캐스트 식(§12.9.8)와 달리 연산자는 as 예외를 throw하지 않습니다. 대신 표시된 변환을 사용할 수 없는 경우 결과 값은 null.
양식 E as T작업에서 E은 식이어야 하며, T는 참조 형식, 참조 형식으로 알려진 형식 매개 변수, 또는 nullable 값 형식이어야 합니다. 또한 다음 중 하나 이상이 참이어야 하며, 그렇지 않으면 컴파일 시 오류가 발생합니다.
- 식별자(§10.2.2), 암시적 null 허용(§10.2.6), 암시적 참조(§10.2.8), 박싱(§10.2.9), 명시적 null 허용(§10.3.4), 명시적 참조(§10.3.5), 또는 래핑(§8.3.12) 변환이
E에서T으로 존재합니다. -
E또는T형식이 열려 있는 형식입니다. -
E는null리터럴입니다.
컴파일 시간 형식의 E이 dynamic이 아니면, 작업 E as T는 동일한 결과를 생성합니다.
E is T ? (T)(E) : (T)null
단, E 한 번만 평가됩니다. 컴파일러는 위의 확장에 의해 암시된 두 번의 런타임 형식 검사 대신 E as T를 최적화하여 최대 한 번만 런타임 형식 검사를 수행할 수 있을 것으로 기대됩니다.
E 컴파일 시간 형식이 dynamic경우 캐스트 연산자와 달리 as 연산자는 동적으로 바인딩되지 않습니다(§12.3.3). 따라서 이 경우 확장은 다음과 같습니다.
E is T ? (T)(object)(E) : (T)null
사용자 정의 변환과 같은 일부 변환은 as 연산자를 사용할 수 없으며 대신 캐스트 식을 사용하여 수행해야 합니다.
예제: 예제에서
class X { public string F(object o) { return o as string; // OK, string is a reference type } public T G<T>(object o) where T : Attribute { return o as T; // Ok, T has a class constraint } public U H<U>(object o) { return o as U; // Error, U is unconstrained } }
T의 형식 매개 변수G는 클래스 제약 조건이 있으므로 참조 형식으로 알려져 있습니다.U의 형식 매개 변수H은 아니므로as에서H연산자의 사용이 허용되지 않습니다.예제 종료
12.15 논리 연산자
12.15.1 일반
&, ^및 | 연산자를 논리 연산자라고 합니다.
and_expression
: equality_expression
| and_expression '&' equality_expression
;
exclusive_or_expression
: and_expression
| exclusive_or_expression '^' and_expression
;
inclusive_or_expression
: exclusive_or_expression
| inclusive_or_expression '|' exclusive_or_expression
;
논리 연산자의 피연산자가 컴파일 시간 형식이 dynamic인 경우, 식은 동적으로 바인딩됩니다(§12.3.3). 이 경우, 식의 컴파일 시간 형식은 dynamic이고, 아래에 설명된 해상도는 컴파일 시간 형식이 dynamic인 해당 피연산자의 런타임 형식을 사용하여 런타임에 수행됩니다.
«op»가 논리 연산자 중 하나인 x «op» y양식의 연산의 경우 특정 연산자 구현을 선택하기 위해 오버로드 확인(§12.4.5)이 적용됩니다. 피연산자는 선택한 연산자의 매개 변수 형식으로 변환되고 결과 형식은 연산자의 반환 형식입니다.
미리 정의된 논리 연산자는 다음 하위클래스에 설명되어 있습니다.
12.15.2 정수 논리 연산자
미리 정의된 정수 논리 연산자는 다음과 같습니다.
int operator &(int x, int y);
uint operator &(uint x, uint y);
long operator &(long x, long y);
ulong operator &(ulong x, ulong y);
int operator |(int x, int y);
uint operator |(uint x, uint y);
long operator |(long x, long y);
ulong operator |(ulong x, ulong y);
int operator ^(int x, int y);
uint operator ^(uint x, uint y);
long operator ^(long x, long y);
ulong operator ^(ulong x, ulong y);
& 연산자는 두 피연산자의 비트 논리 AND를 계산하고, | 연산자는 두 피연산자의 비트 논리 OR을 계산하고, ^ 연산자는 두 피연산자의 비트 논리 전용 OR을 계산합니다. 이러한 작업에서는 오버플로가 불가능합니다.
리프트된(§12.4.8) 형식은 미리 정의된 비리프트 정수 논리 연산자의 형식도 미리 정의되어 있습니다.
12.15.3 열거형 논리 연산자
E 모든 열거형 형식은 다음과 같이 미리 정의된 논리 연산자를 암시적으로 제공합니다.
E operator &(E x, E y);
E operator |(E x, E y);
E operator ^(E x, E y);
x «op» y과 x가 기본 형식 y의 열거형 E의 표현식이고, «op»이 논리 연산자 중 하나일 때, U의 평가 결과는 (E)((U)x «op» (U)y)의 평가와 정확히 동일합니다. 즉, 열거형 형식 논리 연산자는 두 피연산자의 기본 형식에 대해 논리 연산을 수행하기만 하면 됩니다.
리프팅된(§12.4.8) 미리 정의된 열거형 논리 연산자의 형식도 미리 정의되어 있습니다.
12.15.4 부울 논리 연산자
미리 정의된 부울 논리 연산자는 다음과 같습니다.
bool operator &(bool x, bool y);
bool operator |(bool x, bool y);
bool operator ^(bool x, bool y);
x & y와 true가 모두 x일 경우, y의 결과는 true입니다. 그렇지 않으면 결과가 false.
x | y 또는 true가 x인 경우 y의 결과는 true입니다. 그렇지 않으면 결과가 false.
x ^ y가 true이고 x가 true인 경우에, 혹은 y가 false이고 x이 false인 경우에, y의 결과는 true입니다. 그렇지 않으면 결과가 false. 피연산자가 bool형식이면 ^ 연산자는 != 연산자로 동일한 결과를 계산합니다.
12.15.5 Nullable 부울 및 | 연산자
nullable 부울 형식 bool?은 세 가지 값, true, false, 및 null을 나타낼 수 있습니다.
다른 이진 연산자와 마찬가지로 리프팅된 형태의 논리 연산 & 자 및 | (§12.15.4)도 미리 정의되어 있습니다.
bool? operator &(bool? x, bool? y);
bool? operator |(bool? x, bool? y);
해제된 & 및 | 연산자의 의미 체계는 다음 표에서 정의됩니다.
x |
y |
x & y |
x \| y |
|---|---|---|---|
true |
true |
true |
true |
true |
false |
false |
true |
true |
null |
null |
true |
false |
true |
false |
true |
false |
false |
false |
false |
false |
null |
false |
null |
null |
true |
null |
true |
null |
false |
false |
null |
null |
null |
null |
null |
참고:
bool?형식은 개념적으로 SQL의 불리언 표현에 사용되는 삼진 형식과 비슷합니다. 위의 표는 SQL과 동일한 의미 체계를 따르지만, 및&연산자에는|규칙이 적용되지 않습니다. §12.4.8 규칙은 이미 해제된^연산자에 대한 SQL과 유사한 의미 체계를 제공합니다. 끝 메모
12.16 조건부 논리 연산자
12.16.1 일반
&& 및 || 연산자를 조건부 논리 연산자라고 합니다. "쇼트서킷" 논리 연산자라고도 합니다.
conditional_and_expression
: inclusive_or_expression
| conditional_and_expression '&&' inclusive_or_expression
;
conditional_or_expression
: conditional_and_expression
| conditional_or_expression '||' conditional_and_expression
;
&& 및 || 연산자는 & 및 | 연산자의 조건부 버전입니다.
- 작업
x && y는 작업x & y에 해당하며, 이는y가x가 아닌 경우에만false가 평가된다는 점이 다릅니다. - 작업
x || y는 작업x | y에 해당하며, 이는y가x가 아닌 경우에만true가 평가된다는 점이 다릅니다.
참고: 쇼트 서킷이 '참이 아님' 및 '거짓이 아님' 조건을 사용하는 이유는 사용자 정의 조건 연산자가 쇼트 서킷이 적용되는 시점을 정의할 수 있도록 하기 위함입니다. 사용자 정의 형식은
operator truefalse반환하고operator falsefalse반환하는 상태일 수 있습니다. 이러한 경우&&도||도 단락하지 않습니다. 끝 메모
조건부 논리 연산자의 피연산자에 컴파일 시간 형식이 dynamic인 경우, 식이 동적으로 바인딩된 것으로 간주됩니다(§12.3.3). 이 경우, 식의 컴파일 시간 형식은 dynamic이고, 아래에 설명된 해상도는 컴파일 시간 형식이 dynamic인 해당 피연산자의 런타임 형식을 사용하여 런타임에 수행됩니다.
형식 x && y 또는 x || y 작업은 작업이 또는 x & y작성된 것처럼 오버로드 해석(x | y)을 적용하여 처리됩니다. 그러면
- 오버로드 확인이 최상의 단일 연산자를 찾지 못하거나 오버로드 확인에서 미리 정의된 정수 논리 연산자 또는 nullable 부울 논리 연산자(§12.15.5) 중 하나를 선택하면 바인딩 시간 오류가 발생합니다.
- 그렇지 않은 경우 선택한 연산자가 미리 정의된 부울 논리 연산자(§12.15.4) 중 하나인 경우 이 연산은 §12.16.2에 설명된 대로 처리됩니다.
- 그렇지 않으면 선택한 연산자는 사용자 정의 연산자이며 작업은 §12.16.3에 설명된 대로 처리됩니다.
조건부 논리 연산자를 직접 오버로드할 수 없습니다. 그러나 조건부 논리 연산자는 일반 논리 연산자의 측면에서 평가되기 때문에 일반 논리 연산자의 오버로드는 특정 제한 사항과 함께 조건부 논리 연산자의 오버로드로 간주됩니다. 이 내용은 §12.16.3에 자세히 설명되어 있습니다.
12.16.2 부울 조건부 논리 연산자
&& 또는 ||의 피연산자가 bool형식이거나, 피연산자가 적용 가능한 operator & 또는 operator |를 정의하지 않고 bool로의 암시적 변환을 정의하는 형식인 경우, 작업은 다음과 같이 처리됩니다.
- 작업
x && y는x ? y : false로 평가됩니다. 즉,x는 먼저 평가되고bool형식으로 변환됩니다. 그런 다음, 만약x가true인 경우,y가 평가되고 형식bool로 변환되며 이것이 작업 결과가 됩니다. 그렇지 않으면 해당 작업의 결과는false입니다. - 작업
x || y는x ? true : y로 평가됩니다. 즉,x는 먼저 평가되고bool형식으로 변환됩니다. 그런 다음x이true이면 작업의 결과가true입니다. 그렇지 않으면y평가되고bool형식으로 변환되며 이는 작업의 결과가 됩니다.
12.16.3 사용자 정의 조건부 논리 연산자
&& 또는 || 피연산자가 적용 가능한 사용자 정의 operator & 또는 operator |이 선언된 형식인 경우, 다음 둘 다 true여야 합니다. 여기서 T은(는) 선택된 연산자가 선언된 형식입니다.
- 선택한 연산자의 각 매개 변수의 반환 형식과 형식은
T합니다. 즉, 연산자는T형식의 두 피연산자의 논리적 AND 또는 논리적 OR을 계산하고T형식의 결과를 반환해야 합니다. -
T에는operator true및operator false선언이 포함되어야 합니다.
이러한 요구 사항 중 하나가 충족되지 않으면 바인딩 시간 오류가 발생합니다. 그렇지 않으면, && 또는 || 작업은 선택된 사용자 정의 연산자와 사용자 정의 operator true 또는 operator false을 결합하여 평가됩니다.
- 작업
x && y는T.false(x) ? x : T.&(x, y)로 평가됩니다. 이때T.false(x)는operator false에서 선언된T을 호출하며,T.&(x, y)는 선택된operator &을 호출합니다. 즉,x먼저 평가되고 결과에 대해operator false호출되어x확실히 거짓인지 확인합니다. 그런 다음,x가 확실히 false인 경우, 작업의 결과는x에 대해 이전에 계산된 값이 됩니다. 그렇지 않으면y이 평가되고, 선택된operator &이 이전에x에 대해 계산된 값과 방금y에 대해 계산된 값을 이용해 작업의 결과를 생성하도록 호출됩니다. - 작업
x || y는T.true(x) ? x : T.|(x, y)로 평가됩니다. 이때T.true(x)는operator true에서 선언된T을 호출하며,T.|(x, y)는 선택된operator |을 호출합니다. 즉,x먼저 평가되고 결과에 대해operator true호출되어x확실히 참인지 확인합니다. 그런 다음,x가 확실히 true이면 작업의 결과는x에 대해 이전에 계산된 값입니다. 그렇지 않으면y이 평가되고, 선택된operator |이 이전에x에 대해 계산된 값과 방금y에 대해 계산된 값을 이용해 작업의 결과를 생성하도록 호출됩니다.
이러한 작업 중 하나에서, x로 주어진 식은 단 한 번만 평가되며, y로 주어진 식은 평가되지 않거나 정확히 한 번만 평가됩니다.
12.17 null 병합 연산자
?? 연산자를 null 병합 연산자라고 합니다.
null_coalescing_expression
: conditional_or_expression
| conditional_or_expression '??' null_coalescing_expression
| throw_expression
;
a ?? b형식의 null 병합 식에서 a이 비null인 경우, 결과는 a이며, 그렇지 않으면 결과가 b입니다.
b이 a인 경우에만 작업이 null를 평가합니다.
null 병합 연산자는 우측 결합 연산자이므로 연산이 오른쪽에서 왼쪽으로 그룹화됩니다.
예제:
a ?? b ?? c형태의 식은a ?? (b ?? c)로 평가됩니다. 일반적으로 말해서,E1 ?? E2 ?? ... ?? EN형태의 식은null아닌 피연산자 중 첫 번째를 반환하고, 모든 피연산자가null인 경우에는null를 반환합니다. 예제 종료
a ?? b 식의 형식은 피연산자에서 사용할 수 있는 암시적 변환에 따라 달라집니다. 기본 설정 순서대로 a ?? b의 형식은 A₀, A, 또는 B입니다. 여기서 A는 a의 형식이고 (a에 형식이 있는 경우), B는 b의 형식입니다 (b에 형식이 있는 경우). A₀는 A가 nullable 값 형식인 경우 A의 기본 형식이거나, 그렇지 않으면 A입니다. 특히 a ?? b 다음과 같이 처리됩니다.
- 관리되지 않는 형식(
A)이 있거나 null을 허용하지 않는 값 형식으로 알려진 경우 컴파일 시간 오류가 발생합니다. - 그렇지 않으면
A있고b동적 식이면 결과 형식이dynamic. 런타임에a먼저 평가됩니다.a이null이 아니면,a가dynamic로 변환되고, 이것이 결과가 됩니다. 그렇지 않으면b가 평가되어 그것이 결과가 됩니다. - 그렇지 않은 경우,
A이 존재하고 NULL 허용 값 형식이며,b에서A₀로의 암시적 변환이 존재할 경우, 결과 형식은A₀입니다. 런타임에a먼저 평가됩니다.a이null이 아니면a가 형식A₀로 래핑 해제되어 이 결과가 됩니다. 그렇지 않으면b가 평가되어 형식A₀로 변환되고 결과가 됩니다. - 그렇지 않으면,
A이 존재하고b에서A로의 암시적 변환이 존재하면, 결과 형식은A입니다. 런타임에a먼저 평가됩니다.a이null이 아니면a이 결과가 됩니다. 그렇지 않으면b가 평가되어 형식A로 변환되고 결과가 됩니다. - 그렇지 않으면,
A이 존재하고 null 허용 값 형식이며,b이B형식이고A₀에서B로의 암시적 변환이 있으면, 결과 형식은B입니다. 런타임에a먼저 평가됩니다.a가null와 같지 않으면,a가A₀로 래핑이 해제되고B형식으로 변환됩니다. 이것이 결과가 됩니다. 그렇지 않으면,b이 평가되어 결과가 되는 것입니다. - 그 외에,
b이B형식이며a에서B로 암시적 변환이 존재하면, 결과 형식은B입니다. 런타임에a먼저 평가됩니다.a가null이 아니면,a는B형식으로 변환되어 결과입니다. 그렇지 않으면,b이 평가되어 결과가 되는 것입니다. - 그렇지 않으면
a및b호환되지 않으며 컴파일 시간 오류가 발생합니다.
예제:
T M<T>(T a, T b) => a ?? b; string s = M(null, "text"); int i = M(19, 23);메서드
T의 형식 매개 변수M는 제한되지 않습니다. 따라서 형식 인수는 첫 번째 호출M에서와 같이 참조 형식 또는 null 허용 값 형식일 수 있습니다. 형식 인수는 두 번째 호출M에 표시된 대로 null을 허용하지 않는 값 형식일 수도 있습니다. 형식 인수가 nullable이 아닌 값 형식인 경우 식a ?? b의 값은a.예제 종료
12.18 throw 식 연산자
throw_expression
: 'throw' null_coalescing_expression
;
throw_expression은 null_coalescing_expression을 평가하여 생성된 값을 throw합니다. 식은 암시적으로 System.Exception으로 변환될 수 있어야 하며, 식을 계산한 결과는 throw하기 전에 System.Exception으로 변환됩니다.
throw 식 계산의 런타임 동작은 throw 문(§13.10.6)에 지정된 동작과 동일합니다.
throw_expression는 유형이 없습니다. throw_expression은 모든 형식으로의 변환이 가능합니다, 이는 암시적 throw 변환을 통해서입니다.
throw 식은 다음과 같은 구문적 맥락에서만 발생해야 합니다.
- 3항 조건 연산자(
?:)의 두 번째 또는 세 번째 피연산자입니다. - null 병합 연산자(
??)의 두 번째 피연산자입니다. - 식 본문을 가진 람다 또는 멤버.
12.19 선언 식
선언 식은 지역 변수를 선언합니다.
declaration_expression
: local_variable_type identifier
;
local_variable_type
: type
| 'var'
;
단순 이름 조회에서 연결된 선언(§12.8.4)을 찾지 못한 경우에도 _ 선언 식으로 간주됩니다. 갱제 표현으로 사용될 때, _는 단순 무시라고 불립니다. 의미상 var _동일하지만 더 많은 위치에서 허용됩니다.
선언 식은 다음 구문 컨텍스트에서만 발생합니다.
-
outargument_value 을 argument_list에서. - 단순 배정의 왼쪽을 구성하는 단순 무시
_(§12.23.2)입니다. - 재귀적으로 중첩된 하나 이상의 tuple_expression내에서 tuple_element로서, 가장 바깥쪽은 분해할당의 왼쪽을 구성합니다. deconstruction_expression 선언식이 구문적으로 존재하지 않더라도, 이 위치에서 선언식이 생성됩니다.
참고: 선언 식을 괄호로 지정할 수 없음을 의미합니다. 끝 메모
암시적으로 형식화된 변수가 declaration_expression 선언으로 선언된 경우, 그 변수를 선언된 argument_list 내에서 참조하는 것은 오류입니다.
declaration_expression로 선언된 변수가 발생하는 분해 할당 내에서 참조되는 경우, 이는 오류입니다.
단순히 버려지거나 local_variable_type이 식별자 var인 선언식은 암시적으로 형식화된 변수로 분류됩니다. 식에는 형식이 없으며 다음과 같이 구문 컨텍스트에 따라 지역 변수의 형식이 유추됩니다.
- argument_list 변수의 유추 형식은 해당 매개 변수의 선언된 형식입니다.
- 단순 할당에서 왼쪽에 있는 변수의 추론된 유형은 할당의 오른쪽 측의 유형입니다.
- 단순 할당의 왼쪽에 있는 tuple_expression 변수의 유추 형식은 할당의 오른쪽(분해 후)에 있는 해당 튜플 요소의 형식입니다.
그렇지 않으면, 선언식은 명시적으로 형식화된 변수로 분류되고, 선언된 변수와 식의 유형은 local_variable_type에서 주어진 대로 지정되어야 합니다.
식별자 _를 가진 선언식은 버리기(§9.2.9.2)이며, 변수의 이름을 도입하지 않습니다.
_ 이외의 식별자가 있는 선언 식은 이 이름을 가장 가까운 지역 변수 선언 공간(§7.3)에 도입합니다.
예제:
string M(out int i, string s, out bool b) { ... } var s1 = M(out int i1, "One", out var b1); Console.WriteLine($"{i1}, {b1}, {s1}"); // Error: i2 referenced within declaring argument list var s2 = M(out var i2, M(out i2, "Two", out bool b2), out b2); var s3 = M(out int _, "Three", out var _);
s1선언은 명시적 및 암시적으로 형식화된 선언 식을 모두 보여 줍니다.b1의 추론된 형식은bool의 출력 매개변수에 해당하는 형식이므로M1입니다. 후속WriteLine은 바깥쪽 범위에 도입된i1및b1에 액세스할 수 있습니다.
s2선언은i2선언된 인수 목록 내에서 참조가 발생하므로 허용되지 않는M대한 중첩된 호출에서i2사용하려는 시도를 보여 줍니다. 반면에 마지막 인수의b2대한 참조는b2선언된 중첩된 인수 목록의 끝 후에 발생하므로 허용됩니다.
s3선언은 무시되는 암시적 및 명시적으로 형식화된 선언 식의 사용을 보여 줍니다. "디스카드를 사용하면 명명된 변수를 선언하지 않기 때문에_식별자가 여러 번 발생할 수 있습니다."(int i1, int _, (var i2, var _), _) = (1, 2, (3, 4), 5);이 예제에서는 분해 할당에서 변수와 삭제 모두에 대해 암시적 및 명시적으로 형식화된 선언 식을 사용하는 방법을 보여 줍니다. simple_name
_는var _가 선언되지 않은 경우_와 동일합니다.void M1(out int i) { ... } void M2(string _) { M1(out _); // Error: `_` is a string M1(out var _); }이 예제에서는 바깥쪽 범위에서 변수를 지정하기 때문에
var _을 사용할 수 없는 경우 암시적으로 형식화된 삭제를 제공하기 위해_을 사용하는 방법을 보여 줍니다.예제 종료
12.20 조건부 연산자
?: 연산자를 조건부 연산자라고 합니다. 이 연산자는 때때로 3항 연산자라고도 합니다.
conditional_expression
: null_coalescing_expression
| null_coalescing_expression '?' expression ':' expression
| null_coalescing_expression '?' 'ref' variable_reference ':'
'ref' variable_reference
;
throw 식(§12.18)이 있는 경우 ref 조건부 연산자에서 허용되지 않습니다.
b ? x : y 양식의 조건식은 먼저 조건 b평가합니다. 그런 다음 b이 true인 경우에 x가 평가되어 작업의 결과가 됩니다. 그렇지 않으면 y는 평가되고 연산의 결과가 됩니다. 조건식은 x 및 y모두 평가하지 않습니다.
조건부 연산자는 오른쪽 연결 연산자입니다. 즉, 작업이 오른쪽에서 왼쪽으로 그룹화됩니다.
예제:
a ? b : c ? d : e형태의 식은a ? b : (c ? d : e)로 평가됩니다. 예제 종료
?: 연산자의 첫 번째 피연산자는 암시적으로 bool변환할 수 있는 식이거나 operator true구현하는 형식의 식이어야 합니다. 이러한 요구 사항이 모두 충족되지 않으면 컴파일 시간 오류가 발생합니다.
ref 있는 경우:
- ID 변환은 두 variable_reference형식 사이에 존재해야 하며 결과의 형식은 두 형식 중 하나일 수 있습니다. 두 형식 중 하나가
dynamic경우 형식 유추는dynamic(§8.7)를 선호합니다. 두 형식 중 하나가 튜플 형식(§8.3.11)인 경우 두 튜플에서 동일한 서수 위치의 요소 이름이 일치하는 경우 형식 유추에 요소 이름이 포함됩니다. - 결과는 변수 참조로, 두 variable_reference모두 쓸 수 있는 경우에만 쓸 수 있습니다.
참고:
ref가 존재하는 경우 조건식는 변수 참조를 반환하며, 이는= ref연산자를 사용하여 참조 변수에 할당되거나 참조/입력/출력 매개 변수로 전달될 수 있습니다. 끝 메모
ref가 없는 경우, x 연산자의 두 번째 및 세 번째 피연산자인 y 및 ?:가 조건식의 유형을 제어합니다.
-
x가X형식이고y가Y형식일 때,- ID 변환이 있는 경우
XY결과는 식 집합의 가장 일반적인 형식입니다(§12.6.3.16). 두 형식 중 하나가dynamic경우 형식 유추는dynamic(§8.7)를 선호합니다. 두 형식 중 하나가 튜플 형식(§8.3.11)인 경우 두 튜플에서 동일한 서수 위치의 요소 이름이 일치하는 경우 형식 유추에 요소 이름이 포함됩니다. - 그렇지 않으면 에서
X로의 암시적 변환(Y)은 존재하지만,Y에서X로의 변환이 존재하지 않는 경우,Y이 조건식의 형식이 됩니다. - 그렇지 않으면, 에서
X으로 암시적 열거형 변환(Y)이 존재하는 경우, 그러면Y이(가) 조건식의 형식입니다. - 그렇지 않으면, 에서
Y으로 암시적 열거형 변환(X)이 존재하는 경우, 그러면X이(가) 조건식의 형식입니다. - 그렇지 않으면 에서
Y로의 암시적 변환(X)은 존재하지만,X에서Y로의 변환이 존재하지 않는 경우,X이 조건식의 형식이 됩니다. - 그렇지 않으면 식 형식을 확인할 수 없으며 컴파일 시간 오류가 발생합니다.
- ID 변환이 있는 경우
-
x및y중 하나에만 형식이 있고x및y모두 해당 형식으로 암시적으로 변환할 수 있는 경우 조건식의 형식입니다. - 그렇지 않으면 식 형식을 확인할 수 없으며 컴파일 시간 오류가 발생합니다.
양식 b ? ref x : ref y ref 조건식의 런타임 처리는 다음 단계로 구성됩니다.
- 먼저
b이 평가되고,bool의b값이 결정됩니다.-
b형식에서bool으로 암시적 변환이 존재하는 경우, 이 암시적 변환이 수행되어bool값을 생성합니다. - 그렇지 않으면
operator true형식으로 정의된b호출되어bool값을 생성합니다.
-
- 위의 단계에서 생성한
bool값이true경우x계산되고 결과 변수 참조가 조건식의 결과가 됩니다. - 그렇지 않으면
y이 평가되고, 그 결과 변수의 참조가 조건식의 결과가 됩니다.
b ? x : y 양식의 조건식의 런타임 처리는 다음 단계로 구성됩니다.
- 먼저
b이 평가되고,bool의b값이 결정됩니다.-
b형식에서bool으로 암시적 변환이 존재하는 경우, 이 암시적 변환이 수행되어bool값을 생성합니다. - 그렇지 않으면
operator true형식으로 정의된b호출되어bool값을 생성합니다.
-
- 위의 단계에서 생성한
bool값이true이라면,x가 평가되고 조건식의 형식으로 변환되어 조건식의 결과가 됩니다. - 그렇지 않으면
y가 평가되고 조건식의 형식으로 변환되어, 이는 조건식의 결과가 됩니다.
12.21 익명 함수 식
12.21.1 일반
익명 함수 "인라인" 메서드 정의를 나타내는 식입니다. 무명 함수에는 값이나 형식 자체가 없지만 호환되는 대리자 또는 식 트리 형식으로 변환할 수 있습니다. 무명 함수 변환의 평가는 변환의 대상 형식에 따라 달라집니다. 대리자 형식인 경우 변환은 익명 함수가 정의하는 메서드를 참조하는 대리자 값으로 평가됩니다. 식 트리 형식인 경우 변환은 메서드의 구조를 개체 구조로 나타내는 식 트리로 계산됩니다.
참고: 역사적인 이유로 익명 함수에는 두 가지 구문 체계, 즉 lambda_expression및 anonymous_method_expression이(가) 존재합니다. 거의 모든 목적에 대해 lambda_expression은 더 간결하고 표현적입니다. 반면에, anonymous_method_expression은 이전 버전과의 호환성을 위해 언어에 남아 있습니다. 끝 메모
lambda_expression
: 'async'? anonymous_function_signature '=>' anonymous_function_body
;
anonymous_method_expression
: 'async'? 'delegate' explicit_anonymous_function_signature? block
;
anonymous_function_signature
: explicit_anonymous_function_signature
| implicit_anonymous_function_signature
;
explicit_anonymous_function_signature
: '(' explicit_anonymous_function_parameter_list? ')'
;
explicit_anonymous_function_parameter_list
: explicit_anonymous_function_parameter
(',' explicit_anonymous_function_parameter)*
;
explicit_anonymous_function_parameter
: anonymous_function_parameter_modifier? type identifier
;
anonymous_function_parameter_modifier
: 'ref'
| 'out'
| 'in'
;
implicit_anonymous_function_signature
: '(' implicit_anonymous_function_parameter_list? ')'
| implicit_anonymous_function_parameter
;
implicit_anonymous_function_parameter_list
: implicit_anonymous_function_parameter
(',' implicit_anonymous_function_parameter)*
;
implicit_anonymous_function_parameter
: identifier
;
anonymous_function_body
: null_conditional_invocation_expression
| expression
| 'ref' variable_reference
| block
;
anonymous_function_body를 인식할 때, null_conditional_invocation_expression 및 식 대안이 모두 적용될 경우, 전자를 선택해야 합니다.
참고: 여기에 있는 대안의 겹침과 우선 순위는 설명적인 편의를 위해서만 사용됩니다. 겹침을 제거하기 위해 문법 규칙을 자세히 설명할 수 있습니다. ANTLR 및 기타 문법 시스템은 동일한 편의를 제공하므로, anonymous_function_body는 자동으로 지정된 의미 체계를 갖게 됩니다. 끝 메모
참고: 식처럼 취급될 때,
x?.M()결과 형식이M(void)이면, 와 같은 구문 형식은 오류가 됩니다. 그러나 null_conditional_invocation_expression으로 처리될 때, 결과 형식은void이 될 수 있습니다. 끝 메모
예제: 결과 형식이
List<T>.Reverse의void입니다. 다음 코드에서 익명 식 본문은 null_conditional_invocation_expression이기 때문에 오류가 아닙니다.Action<List<int>> a = x => x?.Reverse();예제 종료
=> 연산자는 대입(=)과 동일한 우선 순위가 있으며 오른쪽 결합성을 가집니다.
한정자가 있는 async 무명 함수는 비동기 함수이며 §15.14에 설명된 규칙을 따릅니다.
lambda_expression 형식의 무명 함수의 매개 변수는 명시적으로 또는 암시적으로 입력할 수 있습니다. 명시적으로 형식화된 매개 변수 목록에서 각 매개 변수의 형식은 명시적으로 명시되어 있습니다. 암시적으로 형식화된 매개 변수 목록에서 매개 변수의 형식은 무명 함수가 발생하는 컨텍스트에서 유추됩니다. 특히 무명 함수가 호환되는 대리자 형식 또는 식 트리 형식으로 변환될 때 해당 형식은 매개 변수 형식(§10.7)을 제공합니다.
암시적으로 형식화된 단일 매개 변수가 있는 lambda_expression 매개 변수 목록에서 괄호를 생략할 수 있습니다. 즉, 특정 형태의 익명 함수입니다.
( «param» ) => «expr»
로 축약할 수 있습니다.
«param» => «expr»
anonymous_method_expression 형식의 무명 함수의 매개 변수 목록은 선택 사항입니다. 지정된 경우 매개 변수를 명시적으로 입력해야 합니다. 그렇지 않은 경우 무명 함수는 출력 매개 변수를 포함하지 않는 매개 변수 목록이 있는 대리자로 변환할 수 있습니다.
익명 함수의 블록 본문은 항상 도달할 수 있습니다(§13.2).
예제: 익명 함수의 몇 가지 예는 다음과 같습니다.
x => x + 1 // Implicitly typed, expression body x => { return x + 1; } // Implicitly typed, block body (int x) => x + 1 // Explicitly typed, expression body (int x) => { return x + 1; } // Explicitly typed, block body (x, y) => x * y // Multiple parameters () => Console.WriteLine() // No parameters async (t1,t2) => await t1 + await t2 // Async delegate (int x) { return x + 1; } // Anonymous method expression delegate { return 1 + 1; } // Parameter list omitted예제 종료
lambda_expression및 anonymous_method_expression의 행동은 다음 점을 제외하고 동일합니다.
- anonymous_method_expression은 매개변수 목록을 완전히 생략하여 수치 매개변수 목록의 대리자 형식으로 변환될 수 있습니다.
- lambda_expression매개 변수 형식을 생략하거나 유추할 수 있는 반면, anonymous_method_expression매개 변수 형식을 명시해야 합니다.
- lambda_expression 본문은 식 또는 블록일 수 있지만 anonymous_method_expression 본문은 블록이어야 합니다.
- 오직 lambda_expression만이 호환되는 식 트리 형식으로 변환됩니다(§8.6).
12.21.2 익명 함수 서명
익명 함수 시그니처 anonymous_function_signature 는 익명 함수에 대한 매개변수의 이름 및 선택적으로 형식을 정의합니다. 무명 함수의 매개 변수 범위는 anonymous_function_body(§7.7)입니다. 매개 변수 목록(지정된 경우)과 함께 무명 메서드 본문은 선언 공간(§7.3)을 구성합니다. 따라서 익명 함수의 매개 변수 이름이 anonymous_method_expression 또는 lambda_expression범위에 포함된 지역 변수, 지역 상수 또는 매개 변수의 이름과 일치하는 것은 컴파일 시간 오류입니다.
무명 함수에 explicit_anonymous_function_signature이 있는 경우, 매개 변수 형식과 한정자가 동일한 순서로 있는 형식의 호환되는 대리자 형식과 식 트리 형식 집합으로 제한됩니다 (§10.7). 메서드 그룹 변환(§10.8)과는 달리, 익명 함수 매개변수 타입의 반공변성은 지원되지 않습니다. 익명 함수에 anonymous_function_signature없는 경우 호환되는 대리자 형식 및 식 트리 형식 집합이 출력 매개 변수가 없는 형식으로 제한됩니다.
참고로, anonymous_function_signature는 속성이나 매개 변수 배열을 포함할 수 없습니다. 그럼에도 불구하고, 매개 변수 목록에 매개 변수 배열이 포함된 대리자 형식과 anonymous_function_signature이 호환될 수 있습니다.
또한 식 트리 형식으로의 변환은 호환되더라도 컴파일 타임(§8.6)에도 실패할 수 있습니다.
12.21.3 익명 함수 본문
익명 함수의 본문(식 또는 블록)에는 다음 규칙이 적용됩니다.
- 익명 함수에 서명이 포함된 경우 서명에 지정된 매개 변수를 본문에서 사용할 수 있습니다. 무명 함수에 서명이 없으면 매개 변수(§10.7)가 있는 대리자 형식 또는 식 형식으로 변환할 수 있지만 본문에서는 매개 변수에 액세스할 수 없습니다.
- 가장 가까운 익명 함수의 서명(있는 경우)에 지정된 참조별 매개 변수를 제외하고 본문이 참조 매개 변수에 액세스하는 것은 컴파일 시간 오류입니다.
- 가장 가까운 익명 함수의 서명(있는 경우)에 지정된 매개 변수를 제외하고 본문이
ref struct형식의 매개 변수에 액세스하는 것은 컴파일 시간 오류입니다. -
this형식이 구조체 형식인 경우 본문이this액세스하는 것은 컴파일 시간 오류입니다. 이는 액세스가 명시적(this.x)이든 암시적이든(x구조체의 인스턴스 멤버인x)이든 마찬가지입니다. 이 규칙은 단순히 이러한 액세스를 금지하며 멤버 조회로 인해 구조체의 멤버가 생성되는지 여부에 영향을 주지 않습니다. - 본문은 익명 함수의 외부 변수(§12.21.6)에 액세스할 수 있습니다. 외부 변수의 액세스는 lambda_expression 또는 anonymous_method_expression 평가될 때 활성 상태인 변수의 인스턴스를 참조합니다(§12.21.7).
- 본문에
goto문,break문 또는 대상이 본문 외부 또는 포함된 익명 함수의 본문 내에 있는continue문을 포함하는 것은 컴파일 시간 오류입니다. - 본문의
return문은 가장 가까운 익명 함수에서 호출하여 제어권을 반환하며, 이는 바깥쪽 함수 멤버로부터가 아닙니다.
익명 함수 블록을 lambda_expression 또는 anonymous_method_expression평가 및 호출이 아닌 다른 방법으로 실행할 수 있는지 여부는 명확히 정해져 있지 않습니다. 특히 컴파일러는 하나 이상의 명명된 메서드 또는 형식을 합성하여 익명 함수를 구현하도록 선택할 수 있습니다. 이러한 합성된 요소의 이름은 컴파일러 사용을 위해 예약된 형식이어야 합니다(§6.4.3).
12.21.4 오버로드 확인
인수 목록의 익명 함수는 형식 유추 및 오버로드 확인에 참여합니다. 정확한 규칙은 §12.6.3 및 §12.6.4 참조하세요.
예제: 다음 예제에서는 무명 함수가 오버로드 해석에 미치는 영향을 보여 줍니다.
class ItemList<T> : List<T> { public int Sum(Func<T, int> selector) { int sum = 0; foreach (T item in this) { sum += selector(item); } return sum; } public double Sum(Func<T, double> selector) { double sum = 0; foreach (T item in this) { sum += selector(item); } return sum; } }
ItemList<T>클래스에는 두 가지Sum메서드가 있습니다. 각각은 목록 항목에서 합계를 계산할 값을 추출하는selector인수를 사용합니다. 추출된 값은int또는double수 있으며 결과 합계도 마찬가지로int또는double.예를 들어
Sum메서드를 사용하여 세부 정보 줄 목록의 합계를 순서대로 계산할 수 있습니다.class Detail { public int UnitCount; public double UnitPrice; ... } class A { void ComputeSums() { ItemList<Detail> orderDetails = GetOrderDetails( ... ); int totalUnits = orderDetails.Sum(d => d.UnitCount); double orderTotal = orderDetails.Sum(d => d.UnitPrice * d.UnitCount); ... } ItemList<Detail> GetOrderDetails( ... ) { ... } }첫 번째로
orderDetails.Sum를 호출할 때, 익명 함수Sum가d => d.UnitCount및Func<Detail,int>와 호환되기 때문에 두 가지Func<Detail,double>메서드를 모두 적용할 수 있습니다. 그러나 오버로드 해제는Sum변환이Func<Detail,int>변환보다 더 낫기 때문에 첫 번째Func<Detail,double>메서드를 선택합니다.
orderDetails.Sum두 번째 호출에서는 무명 함수Sumd => d.UnitPrice * d.UnitCount형식의 값을 생성하기 때문에 두 번째double메서드만 적용할 수 있습니다. 따라서 오버로드 해제는 해당 호출에 대해 두 번째Sum메서드를 선택하게 됩니다.예제 종료
12.21.5 익명 함수 및 동적 바인딩
무명 함수는 동적으로 바인딩된 작업의 수신기, 인수 또는 피연산자가 될 수 없습니다.
12.21.6 외부 변수
12.21.6.1 일반
lambda_expression 또는 anonymous_method_expression의 범위에 포함된 모든 지역 변수, 값 매개 변수 또는 매개 변수 배열은 익명 함수의 외부 변수이라고 합니다. 클래스의 인스턴스 함수 멤버에서 이 값은 값 매개 변수로 간주되며 함수 멤버 내에 포함된 익명 함수의 외부 변수입니다.
12.21.6.2 캡처된 외부 변수
익명 함수에서 외부 변수를 참조하는 경우 외부 변수는 익명 함수에 의해
예제: 예제에서
delegate int D(); class Test { static D F() { int x = 0; D result = () => ++x; return result; } static void Main() { D d = F(); Console.WriteLine(d()); Console.WriteLine(d()); Console.WriteLine(d()); } }
x지역 변수는 익명 함수에 의해 캡처되며,x에서 반환된 대리자가 가비지 수집 대상이 될 때까지 최소한F의 수명이 연장됩니다. 익명 함수의 각 호출은 동일한x인스턴스에서 작동하므로 예제의 출력은 다음과 같습니다.1 2 3예제 종료
무명 함수에서 지역 변수 또는 값 매개 변수를 캡처하는 경우 지역 변수 또는 매개 변수는 더 이상 고정 변수(§24.4)로 간주되지 않고 이동 가능한 변수로 간주됩니다. 그러나 캡처된 외부 변수는 문(fixed)에서 사용할 수 없으므로 캡처된 외부 변수의 주소를 사용할 수 없습니다.
참고: 캡처되지 않은 변수와 달리 캡처된 지역 변수는 여러 실행 스레드에 동시에 노출될 수 있습니다. 끝 메모
12.21.6.3 지역 변수 인스턴스화
로컬 변수는 실행이 변수의 범위에 들어갈 때 인스턴스화된 것으로 간주됩니다.
예제: 예를 들어 다음 메서드가 호출되면
x지역 변수가 인스턴스화되고 루프의 각 반복에 대해 한 번씩 세 번 초기화됩니다.static void F() { for (int i = 0; i < 3; i++) { int x = i * 2 + 1; ... } }그러나 루프 외부로
x선언을 이동하면x이 하나의 인스턴스로 생성됩니다.static void F() { int x; for (int i = 0; i < 3; i++) { x = i * 2 + 1; ... } }예제 종료
캡처되지 않은 경우 지역 변수가 인스턴스화되는 빈도를 정확하게 관찰할 수 없습니다. 인스턴스화의 수명이 서로 연결되지 않기 때문에 각 인스턴스화에서 동일한 스토리지 위치를 사용하기만 하면 됩니다. 그러나 익명 함수가 지역 변수를 캡처하면 인스턴스화의 효과가 명백해집니다.
예제: 예제
delegate void D(); class Test { static D[] F() { D[] result = new D[3]; for (int i = 0; i < 3; i++) { int x = i * 2 + 1; result[i] = () => Console.WriteLine(x); } return result; } static void Main() { foreach (D d in F()) { d(); } } }출력을 생성합니다:
1 3 5그러나
x선언이 루프 외부로 이동되는 경우:delegate void D(); class Test { static D[] F() { D[] result = new D[3]; int x; for (int i = 0; i < 3; i++) { x = i * 2 + 1; result[i] = () => Console.WriteLine(x); } return result; } static void Main() { foreach (D d in F()) { d(); } } }출력은 다음과 같습니다.
5 5 5컴파일러는 세 인스턴스를 단일 대리자 인스턴스(§10.7.2)로 최적화할 수 있습니다(필수는 아님).
예제 종료
for-loop가 반복 변수를 선언하는 경우 해당 변수 자체는 루프 외부에서 선언된 것으로 간주됩니다.
예제: 따라서 반복 변수 자체를 캡처하도록 예제가 변경된 경우:
delegate void D(); class Test { static D[] F() { D[] result = new D[3]; for (int i = 0; i < 3; i++) { result[i] = () => Console.WriteLine(i); } return result; } static void Main() { foreach (D d in F()) { d(); } } }반복 변수의 인스턴스 하나만 캡처되어 출력을 생성합니다.
3 3 3예제 종료
익명 함수 대리자는 캡처된 일부 변수를 공유하지만 다른 변수의 인스턴스는 별도로 가질 수 있습니다.
예제: 예를 들어
F로 변경된 경우static D[] F() { D[] result = new D[3]; int x = 0; for (int i = 0; i < 3; i++) { int y = 0; result[i] = () => Console.WriteLine($"{++x} {++y}"); } return result; }세 대리자는 동일한
x인스턴스를 캡처하지만 별도의y인스턴스를 캡처하며 출력은 다음과 같습니다.1 1 2 1 3 1예제 종료
별도의 익명 함수는 외부 변수의 동일한 인스턴스를 캡처할 수 있습니다.
예시:
delegate void Setter(int value); delegate int Getter(); class Test { static void Main() { int x = 0; Setter s = (int value) => x = value; Getter g = () => x; s(5); Console.WriteLine(g()); s(10); Console.WriteLine(g()); } }두 익명 함수는
x지역 변수의 동일한 인스턴스를 캡처하므로 해당 변수를 통해 "통신"할 수 있습니다. 예제의 출력은 다음과 같습니다.5 10예제 종료
12.21.7 익명 함수 식 평가
익명 함수 F는 직접적으로 또는 대리자 생성 식 D의 실행을 통해 항상 대리자 형식 E이나 식 트리 형식 new D(F)로 변환되어야 합니다. 이 변환은 §10.7설명된 대로 익명 함수의 결과를 결정합니다.
12.21.8 구현 예제
이 하위 클래스는 유익합니다.
이 하위 클라우즈는 다른 C# 구문 측면에서 익명 함수 변환의 가능한 구현에 대해 설명합니다. 여기에 설명된 구현은 상업용 C# 컴파일러에서 사용하는 것과 동일한 원칙을 기반으로 하지만 반드시 위임된 구현이 아니며 가능한 유일한 원칙도 아닙니다. 정확한 의미 체계가 이 사양의 범위를 벗어나기 때문에 식 트리로의 변환만 간략하게 언급합니다.
이 하위 클래스의 나머지 부분에서는 다양한 특성을 가진 익명 함수를 포함하는 코드의 몇 가지 예를 제공합니다. 각 예제에 대해 다른 C# 구문만 사용하는 코드로의 해당 변환이 제공됩니다. 예제에서 식별자 D 다음 대리자 형식을 나타내는 것으로 간주됩니다.
public delegate void D();
익명 함수의 가장 간단한 형태는 외부 변수를 캡처하지 않는 함수입니다.
delegate void D();
class Test
{
static void F()
{
D d = () => Console.WriteLine("test");
}
}
이는 익명 함수의 코드가 배치된 컴파일러 생성 정적 메서드를 참조하는 대리자 인스턴스화로 번역될 수 있습니다.
delegate void D();
class Test
{
static void F()
{
D d = new D(__Method1);
}
static void __Method1()
{
Console.WriteLine("test");
}
}
다음 예제에서 익명 함수는 this인스턴스 멤버를 참조합니다.
delegate void D();
class Test
{
int x;
void F()
{
D d = () => Console.WriteLine(x);
}
}
익명 함수의 코드를 포함하는 컴파일러 생성 인스턴스 메서드로 변환할 수 있습니다.
delegate void D();
class Test
{
int x;
void F()
{
D d = new D(__Method1);
}
void __Method1()
{
Console.WriteLine(x);
}
}
이 예제에서 익명 함수는 지역 변수를 캡처합니다.
delegate void D();
class Test
{
void F()
{
int y = 123;
D d = () => Console.WriteLine(y);
}
}
이제 로컬 변수의 수명을 익명 함수 대리자의 수명 이상으로 확장해야 합니다. 이는 지역 변수를 컴파일러에서 생성된 클래스의 필드로 올림으로써 달성할 수 있습니다. 그런 다음 지역 변수 인스턴스화(§12.21.6.3)는 컴파일러 생성 클래스의 인스턴스를 만드는 데 해당하며, 지역 변수에 액세스하는 것은 컴파일러에서 생성된 클래스의 인스턴스에 있는 필드에 액세스하는 데 해당합니다. 또한 익명 함수는 컴파일러 생성 클래스의 인스턴스 메서드가 됩니다.
delegate void D();
class Test
{
void F()
{
__Locals1 __locals1 = new __Locals1();
__locals1.y = 123;
D d = new D(__locals1.__Method1);
}
class __Locals1
{
public int y;
public void __Method1()
{
Console.WriteLine(y);
}
}
}
마지막으로, 다음 익명 함수는 수명이 다른 두 개의 지역 변수뿐만 아니라 this 캡처합니다.
delegate void D();
class Test
{
int x;
void F()
{
int y = 123;
for (int i = 0; i < 10; i++)
{
int z = i * 2;
D d = () => Console.WriteLine(x + y + z);
}
}
}
여기서는 각 블록에서 로컬 변수가 캡처될 때 컴파일러가 생성한 클래스가 만들어져서, 서로 다른 블록의 로컬 변수가 독립적인 수명을 가질 수 있습니다. 내부 블록에 대한 컴파일러 생성 클래스인 __Locals2인스턴스에는 지역 변수 z 및 __Locals1인스턴스를 참조하는 필드가 포함됩니다. 외부 블록을 위한 컴파일러 생성 클래스인 __Locals1의 인스턴스는 y 지역 변수와 둘러싸는 함수 멤버의 this를 참조하는 필드를 포함합니다. 이러한 데이터 구조를 사용하면 __Local2인스턴스를 통해 캡처된 모든 외부 변수에 도달할 수 있으며, 따라서 익명 함수의 코드는 해당 클래스의 인스턴스 메서드로 구현될 수 있습니다.
delegate void D();
class Test
{
int x;
void F()
{
__Locals1 __locals1 = new __Locals1();
__locals1.__this = this;
__locals1.y = 123;
for (int i = 0; i < 10; i++)
{
__Locals2 __locals2 = new __Locals2();
__locals2.__locals1 = __locals1;
__locals2.z = i * 2;
D d = new D(__locals2.__Method1);
}
}
class __Locals1
{
public Test __this;
public int y;
}
class __Locals2
{
public __Locals1 __locals1;
public int z;
public void __Method1()
{
Console.WriteLine(__locals1.__this.x + __locals1.y + z);
}
}
}
익명 함수를 식 트리로 변환할 때도 지역 변수를 캡처하기 위해 여기에 적용된 동일한 기술을 사용할 수 있습니다. 컴파일러에서 생성된 개체에 대한 참조는 식 트리에 저장될 수 있으며, 지역 변수에 대한 액세스는 이러한 개체에 대한 필드 액세스로 표시될 수 있습니다. 이 접근 방식의 장점은 "상승된" 지역 변수를 대리자와 식 트리 간에 공유할 수 있다는 것입니다.
정보 텍스트의 끝입니다.
12.22 쿼리 식
12.22.1 일반
쿼리 식은 SQL 및 XQuery와 같은 관계형 및 계층적 쿼리 언어와 유사한 쿼리에 대한 언어 통합 구문을 제공할 수 있습니다.
query_expression
: from_clause query_body
;
from_clause
: 'from' type? identifier 'in' expression
;
query_body
: query_body_clause* select_or_group_clause query_continuation?
;
query_body_clause
: from_clause
| let_clause
| where_clause
| join_clause
| join_into_clause
| orderby_clause
;
let_clause
: 'let' identifier '=' expression
;
where_clause
: 'where' boolean_expression
;
join_clause
: 'join' type? identifier 'in' expression 'on' expression
'equals' expression
;
join_into_clause
: 'join' type? identifier 'in' expression 'on' expression
'equals' expression 'into' identifier
;
orderby_clause
: 'orderby' orderings
;
orderings
: ordering (',' ordering)*
;
ordering
: expression ordering_direction?
;
ordering_direction
: 'ascending'
| 'descending'
;
select_or_group_clause
: select_clause
| group_clause
;
select_clause
: 'select' expression
;
group_clause
: 'group' expression 'by' expression
;
query_continuation
: 'into' identifier query_body
;
쿼리 식은 from 절로 시작하고 select 또는 group 절로 끝납니다. 초기 from 절 뒤에 0개 이상의 from, let, where, join 또는 orderby 절이 뒤따를 수 있습니다. 각 from 절은 시퀀스요소에 범위가 있는 범위 변수 도입하는 생성기입니다. 각 let 절에는 이전 범위 변수를 통해 계산된 값을 나타내는 범위 변수가 도입됩니다. 각 where 절은 결과에서 항목을 제외하는 필터입니다. 각 join 절은 원본 시퀀스의 지정된 키를 다른 시퀀스의 키와 비교하여 일치하는 쌍을 생성합니다. 각 orderby 절은 지정된 조건에 따라 항목을 다시 정렬합니다. 마지막 select 또는 group 절은 범위 변수를 기준으로 결과의 모양을 지정합니다. 마지막으로, into 절은 한 쿼리의 결과를 후속 쿼리에서 생성기로 처리하여 쿼리를 "스플라이스"하는 데 사용할 수 있습니다.
12.22.2 쿼리 식의 모호성
쿼리 식은 여러 컨텍스트 키워드(§6.4.4): ascending, by, descending, equals, from, group, into, join, let, on, orderby, select 및 where사용합니다.
이러한 식별자를 키워드로 사용하고 간단한 이름으로 사용할 때 발생할 수 있는 모호성을 방지하기 위해 이러한 식별자는 "@" (§6.4.4) 접두사를 지정하지 않는 한 쿼리 식 내의 어느 곳에서나 키워드로 간주됩니다. 이 경우 식별자는 식별자로 간주됩니다. 이를 위해 쿼리 식은 "from식별자"로 시작하고 ";", "=" 또는 ","를 제외한 모든 토큰으로 시작하는 식입니다.
12.22.3 쿼리 식 변환
12.22.3.1 일반
C# 언어는 쿼리 식의 실행 의미 체계를 지정하지 않습니다. 대신 쿼리 식은 쿼리 식 패턴(§12.22.4)을 준수하는 메서드의 호출로 변환됩니다. 특히 쿼리 식은 Where, Select, SelectMany, Join, GroupJoin, OrderBy, OrderByDescending, ThenBy, ThenByDescending, GroupBy, 및 Cast메서드 호출로 변환됩니다. 이러한 메서드는 §12.22.4에 설명된 대로 특정 서명 및 반환 형식을 가질 것으로 예상됩니다. 이러한 메서드는 쿼리되는 개체의 인스턴스 메서드이거나 개체 외부에 있는 확장 메서드일 수 있습니다. 이러한 메서드는 쿼리의 실제 실행을 구현합니다.
쿼리 식에서 메서드 호출로의 변환은 형식 바인딩 또는 오버로드 확인이 수행되기 전에 발생하는 구문 매핑입니다. 쿼리 식을 변환한 후 결과 메서드 호출은 일반 메서드 호출로 처리되며, 이로 인해 컴파일 시간 오류가 발견될 수 있습니다. 이러한 오류 조건에는 존재하지 않는 메서드, 잘못된 형식의 인수 및 형식 유추가 실패하는 제네릭 메서드가 포함됩니다.
쿼리 식은 추가 감소가 가능하지 않을 때까지 다음 번역을 반복적으로 적용하여 처리됩니다. 번역은 애플리케이션 순서대로 나열됩니다. 각 섹션에서는 이전 섹션의 번역이 완전히 수행되었다고 가정하고, 모두 소진된 후에는 동일한 쿼리 식의 처리에서 섹션을 다시 검토하지 않습니다.
쿼리 식에 범위 변수에 대한 할당을 포함하거나 범위 변수를 참조 또는 출력 매개 변수의 인수로 사용하는 것은 컴파일 시간 오류입니다.
특정 변환은 *로 표시된
12.22.3.2 연속이 있는 쿼리 식
쿼리 본문 뒤에 연속 부분이 있는 쿼리 식
from «x1» in «e1» «b1» into «x2» «b2»
로 변환됩니다.
from «x2» in ( from «x1» in «e1» «b1» ) «b2»
다음 섹션의 번역에서는 쿼리에 연속이 없다고 가정합니다.
예제: 예제:
from c in customers group c by c.Country into g select new { Country = g.Key, CustCount = g.Count() }로 번역됩니다.
from g in (from c in customers group c by c.Country) select new { Country = g.Key, CustCount = g.Count() }최종 번역은 다음과 같습니다.
customers. GroupBy(c => c.Country). Select(g => new { Country = g.Key, CustCount = g.Count() })예제 종료
12.22.3.3 명시적 범위 변수 형식
범위 변수 형식을 명시적으로 지정하는 from 절
from «T» «x» in «e»
로 변환됩니다.
from «x» in ( «e» ) . Cast < «T» > ( )
범위 변수 형식을 명시적으로 지정하는 join 절
join «T» «x» in «e» on «k1» equals «k2»
로 변환됩니다.
join «x» in ( «e» ) . Cast < «T» > ( ) on «k1» equals «k2»
다음 섹션의 번역에서는 쿼리에 명시적 범위 변수 형식이 없다고 가정합니다.
예제: 예제
from Customer c in customers where c.City == "London" select c로 변환됩니다.
from c in (customers).Cast<Customer>() where c.City == "London" select c최종 번역은 다음과 입니다.
customers. Cast<Customer>(). Where(c => c.City == "London")예제 종료
참고: 명시적 범위 변수 형식은 제네릭이 아닌
IEnumerable인터페이스를 구현하는 컬렉션을 쿼리하는 데 유용하지만 제네릭IEnumerable<T>인터페이스를 구현하는 경우에는 유용하지 않습니다. 위의 예제에서는 고객이ArrayList유형인 경우입니다. 끝 메모
12.22.3.4 쿼리 식 분해
어떤 양식의 쿼리 식
from «x» in «e» select «x»
로 변환됩니다.
( «e» ) . Select ( «x» => «x» )
예제: 예제
from c in customers select c로 변환됩니다.
(customers).Select(c => c)예제 종료
퇴행성 쿼리 식은 원본의 요소를 사소하게 선택하는 식입니다.
참고: 번역의 이후 단계(§12.22.3.6 및 §12.22.3.7)는 다른 번역 단계에서 도입한 퇴행성 쿼리를 원본으로 대체하여 제거합니다. 그러나 쿼리 식의 결과가 원본 개체 자체가 되지 않도록 하는 것이 중요합니다. 그렇지 않으면 이러한 쿼리의 결과를 반환하면 실수로 프라이빗 데이터(예: 요소 배열)가 호출자에게 노출될 수 있습니다. 따라서 이 단계에서는 소스에서
Select명시적으로 호출하여 소스 코드로 직접 작성된 퇴행성 쿼리를 보호합니다. 그런 다음Select및 기타 쿼리 연산자의 구현자가 이러한 메서드가 원본 개체 자체를 반환하지 않도록 합니다. 끝 메모
12.22.3.5 From, let, where, join 및 orderby 절
두 번째 from 절과 select 절이 있는 쿼리 식
from «x1» in «e1»
from «x2» in «e2»
select «v»
로 변환됩니다.
( «e1» ) . SelectMany( «x1» => «e2» , ( «x1» , «x2» ) => «v» )
예제: 예제
from c in customers from o in c.Orders select new { c.Name, o.OrderID, o.Total }로 변환됩니다.
(customers). SelectMany(c => c.Orders, (c,o) => new { c.Name, o.OrderID, o.Total } )예제 종료
두 번째 from 절을 가진 쿼리 식과 비어 있지 않은 쿼리 본문 절 집합을 포함하는 쿼리 본문 Q.
from «x1» in «e1»
from «x2» in «e2»
Q
로 변환됩니다.
from * in («e1») . SelectMany( «x1» => «e2» ,
( «x1» , «x2» ) => new { «x1» , «x2» } )
Q
예제: 예제
from c in customers from o in c.Orders orderby o.Total descending select new { c.Name, o.OrderID, o.Total }로 변환됩니다.
from * in (customers). SelectMany(c => c.Orders, (c,o) => new { c, o }) orderby o.Total descending select new { c.Name, o.OrderID, o.Total }최종 번역은 다음과 입니다.
customers. SelectMany(c => c.Orders, (c,o) => new { c, o }). OrderByDescending(x => x.o.Total). Select(x => new { x.c.Name, x.o.OrderID, x.o.Total })여기서
x표시되지 않으며 액세스할 수 없는 컴파일러 생성 식별자입니다.예제 종료
앞에 있는 let 절과 함께 한 from 표현식:
from «x» in «e»
let «y» = «f»
...
로 변환됩니다.
from * in ( «e» ) . Select ( «x» => new { «x» , «y» = «f» } )
...
예제: 예제
from o in orders let t = o.Details.Sum(d => d.UnitPrice * d.Quantity) where t >= 1000 select new { o.OrderID, Total = t }로 변환됩니다.
from * in (orders).Select( o => new { o, t = o.Details.Sum(d => d.UnitPrice * d.Quantity) }) where t >= 1000 select new { o.OrderID, Total = t }최종 번역은 다음과 입니다.
orders .Select(o => new { o, t = o.Details.Sum(d => d.UnitPrice * d.Quantity) }) .Where(x => x.t >= 1000) .Select(x => new { x.o.OrderID, Total = x.t })여기서
x표시되지 않으며 액세스할 수 없는 컴파일러 생성 식별자입니다.예제 종료
앞에 있는 where 절과 함께 한 from 표현식:
from «x» in «e»
where «f»
...
로 변환됩니다.
from «x» in ( «e» ) . Where ( «x» => «f» )
...
join 절이 바로 뒤에 select 절이 가옵니다.
from «x1» in «e1»
join «x2» in «e2» on «k1» equals «k2»
select «v»
로 변환됩니다.
( «e1» ) . Join( «e2» , «x1» => «k1» , «x2» => «k2» , ( «x1» , «x2» ) => «v» )
예제: 예제
from c in customers join o in orders on c.CustomerID equals o.CustomerID select new { c.Name, o.OrderDate, o.Total }로 변환됩니다.
(customers).Join( orders, c => c.CustomerID, o => o.CustomerID, (c, o) => new { c.Name, o.OrderDate, o.Total })예제 종료
join 절 뒤에 쿼리 본문 절이 잇습니다.
from «x1» in «e1»
join «x2» in «e2» on «k1» equals «k2»
...
로 변환됩니다.
from * in ( «e1» ) . Join(
«e2» , «x1» => «k1» , «x2» => «k2» ,
( «x1» , «x2» ) => new { «x1» , «x2» })
...
join
-
into 절 바로 뒤에 select 절이 따라오는 문장
from «x1» in «e1»
join «x2» in «e2» on «k1» equals «k2» into «g»
select «v»
로 변환됩니다.
( «e1» ) . GroupJoin( «e2» , «x1» => «k1» , «x2» => «k2» ,
( «x1» , «g» ) => «v» )
join into 절 뒤에 쿼리 본문 절이 잇습니다.
from «x1» in «e1»
join «x2» in «e2» on «k1» equals «k2» into *g»
...
로 변환됩니다.
from * in ( «e1» ) . GroupJoin(
«e2» , «x1» => «k1» , «x2» => «k2» , ( «x1» , «g» ) => new { «x1» , «g» })
...
예제: 예제
from c in customers join o in orders on c.CustomerID equals o.CustomerID into co let n = co.Count() where n >= 10 select new { c.Name, OrderCount = n }로 변환됩니다.
from * in (customers).GroupJoin( orders, c => c.CustomerID, o => o.CustomerID, (c, co) => new { c, co }) let n = co.Count() where n >= 10 select new { c.Name, OrderCount = n }최종 번역은 다음과 입니다.
customers .GroupJoin( orders, c => c.CustomerID, o => o.CustomerID, (c, co) => new { c, co }) .Select(x => new { x, n = x.co.Count() }) .Where(y => y.n >= 10) .Select(y => new { y.x.c.Name, OrderCount = y.n })여기서
x및y표시되지 않으며 액세스할 수 없는 컴파일러 생성 식별자입니다.예제 종료
orderby 절 및 앞의 from 절:
from «x» in «e»
orderby «k1» , «k2» , ... , «kn»
...
로 변환됩니다.
from «x» in ( «e» ) .
OrderBy ( «x» => «k1» ) .
ThenBy ( «x» => «k2» ) .
... .
ThenBy ( «x» => «kn» )
...
ordering 절이 내림차순 방향 표시기를 지정하는 경우, 대신 OrderByDescending 또는 ThenByDescending 호출이 생성됩니다.
예제: 예제
from o in orders orderby o.Customer.Name, o.Total descending select o최종 번역본이 있습니다.
(orders) .OrderBy(o => o.Customer.Name) .ThenByDescending(o => o.Total)예제 종료
다음 번역에서는 let, where, join 또는 orderby 절이 없고 각 쿼리 식에 하나의 초기 from 절만 있다고 가정합니다.
12.22.3.6 절 선택
어떤 양식의 쿼리 식
from «x» in «e» select «v»
로 변환됩니다.
( «e» ) . Select ( «x» => «v» )
«v»이 식별자 «x»인 경우를 제외하고 번역은 간단합니다.
( «e» )
예제: 예제
from c in customers.Where(c => c.City == "London") select c단순히 번역됩니다.
(customers).Where(c => c.City == "London")예제 종료
12.22.3.7 그룹 절
group 절
from «x» in «e» group «v» by «k»
로 변환됩니다.
( «e» ) . GroupBy ( «x» => «k» , «x» => «v» )
«v»가 식별자 «x»인 경우를 제외하고, 번역은
( «e» ) . GroupBy ( «x» => «k» )
예제: 예제
from c in customers group c.Name by c.Country로 변환됩니다.
(customers).GroupBy(c => c.Country, c => c.Name)예제 종료
12.22.3.8 투명한 식별자
특정 변환은 투명 식별자가나타내는 *범위 변수를 삽입합니다. 투명 식별자는 쿼리 식 변환 프로세스에서 중간 단계로만 존재합니다.
쿼리 변환에서 투명한 식별자를 삽입하면 추가 변환 단계가 투명 식별자를 익명 함수 및 익명 개체 이니셜라이저로 전파합니다. 이러한 컨텍스트에서 투명 식별자는 다음과 같은 동작을 수행합니다.
- 익명 함수에서 투명 식별자가 매개 변수로 발생하면 연결된 익명 형식의 멤버가 익명 함수 본문의 범위에 자동으로 포함됩니다.
- 투명한 식별자를 가진 멤버가 범위에 있으면 해당 멤버의 멤버도 범위에 있습니다.
- 익명 객체 이니셜라이저에서 투명 식별자가 멤버 선언자로 사용될 때, 이는 투명 식별자를 포함하는 멤버를 추가합니다.
위에서 설명한 변환 단계에서 투명 식별자는 항상 익명 형식과 함께 도입되며, 여러 범위 변수를 단일 개체의 멤버로 캡처합니다. C#의 구현은 익명 형식과 다른 메커니즘을 사용하여 여러 범위 변수를 그룹화할 수 있습니다. 다음 번역 예제에서는 익명 형식이 사용된다고 가정하고 투명한 식별자의 가능한 변환을 보여 줍니다.
예제: 예제
from c in customers from o in c.Orders orderby o.Total descending select new { c.Name, o.Total }로 변환됩니다.
from * in (customers).SelectMany(c => c.Orders, (c,o) => new { c, o }) orderby o.Total descending select new { c.Name, o.Total }추가로 번역되는
customers .SelectMany(c => c.Orders, (c,o) => new { c, o }) .OrderByDescending(* => o.Total) .Select(\* => new { c.Name, o.Total })투명 식별자가 지워질 때 동등하게 되는
customers .SelectMany(c => c.Orders, (c,o) => new { c, o }) .OrderByDescending(x => x.o.Total) .Select(x => new { x.c.Name, x.o.Total })여기서
x표시되지 않으며 액세스할 수 없는 컴파일러 생성 식별자입니다.예제
from c in customers join o in orders on c.CustomerID equals o.CustomerID join d in details on o.OrderID equals d.OrderID join p in products on d.ProductID equals p.ProductID select new { c.Name, o.OrderDate, p.ProductName }로 변환됩니다.
from * in (customers).Join( orders, c => c.CustomerID, o => o.CustomerID, (c, o) => new { c, o }) join d in details on o.OrderID equals d.OrderID join p in products on d.ProductID equals p.ProductID select new { c.Name, o.OrderDate, p.ProductName }으로 더 축소되는
customers .Join(orders, c => c.CustomerID, o => o.CustomerID, (c, o) => new { c, o }) .Join(details, * => o.OrderID, d => d.OrderID, (*, d) => new { *, d }) .Join(products, * => d.ProductID, p => p.ProductID, (*, p) => new { c.Name, o.OrderDate, p.ProductName })최종 번역은 다음과 입니다.
customers .Join(orders, c => c.CustomerID, o => o.CustomerID, (c, o) => new { c, o }) .Join(details, x => x.o.OrderID, d => d.OrderID, (x, d) => new { x, d }) .Join(products, y => y.d.ProductID, p => p.ProductID, (y, p) => new { y.x.c.Name, y.x.o.OrderDate, p.ProductName })여기서
x및y표시되지 않으며 액세스할 수 없는 컴파일러 생성 식별자입니다. 예제 종료
12.22.4 쿼리 식 패턴
쿼리 식 패턴 형식들이 쿼리 식을 지원하기 위해 구현할 수 있는 메서드 패턴을 수립합니다.
제네릭 형식 C<T> 공용 멤버 메서드와 공개적으로 액세스할 수 있는 확장 메서드를 다음 클래스 정의로 바꿀 수 있는 경우 쿼리 식 패턴을 지원합니다. 멤버 및 액세스 가능한 확장 메서드를 제네릭 형식 C<T>의 "셰이프"라고 합니다. 제네릭 형식은 매개 변수와 반환 형식 간의 적절한 관계를 설명하기 위해 사용되지만 제네릭이 아닌 형식의 패턴도 구현할 수 있습니다.
delegate R Func<T1,R>(T1 arg1);
delegate R Func<T1,T2,R>(T1 arg1, T2 arg2);
class C
{
public C<T> Cast<T>() { ... }
}
class C<T> : C
{
public C<T> Where(Func<T,bool> predicate) { ... }
public C<U> Select<U>(Func<T,U> selector) { ... }
public C<V> SelectMany<U,V>(Func<T,C<U>> selector,
Func<T,U,V> resultSelector) { ... }
public C<V> Join<U,K,V>(C<U> inner, Func<T,K> outerKeySelector,
Func<U,K> innerKeySelector, Func<T,U,V> resultSelector) { ... }
public C<V> GroupJoin<U,K,V>(C<U> inner, Func<T,K> outerKeySelector,
Func<U,K> innerKeySelector, Func<T,C<U>,V> resultSelector) { ... }
public O<T> OrderBy<K>(Func<T,K> keySelector) { ... }
public O<T> OrderByDescending<K>(Func<T,K> keySelector) { ... }
public C<G<K,T>> GroupBy<K>(Func<T,K> keySelector) { ... }
public C<G<K,E>> GroupBy<K,E>(Func<T,K> keySelector,
Func<T,E> elementSelector) { ... }
}
class O<T> : C<T>
{
public O<T> ThenBy<K>(Func<T,K> keySelector) { ... }
public O<T> ThenByDescending<K>(Func<T,K> keySelector) { ... }
}
class G<K,T> : C<T>
{
public K Key { get; }
}
위의 메서드는 Func<T1, R> 및 Func<T1, T2, R>제네릭 대리자 형식을 사용하지만 매개 변수 및 반환 형식에서 동일한 관계를 가진 다른 대리자 또는 식 트리 형식을 똑같이 잘 사용할 수 있습니다.
참고:
C<T>또는O<T>결과에서만ThenBy및ThenByDescending메서드를 사용할 수 있도록 하는OrderBy와OrderByDescending간의 권장 관계입니다. 끝 메모
참고: 각 내부 시퀀스에 추가
GroupBy속성이 있는 시퀀스 시퀀스인Key결과의 권장 셰이프입니다. 끝 메모
참고: 쿼리 식은 구문 매핑을 통해 메서드 호출로 변환되므로 형식은 쿼리 식 패턴을 구현하는 방법에 상당한 유연성을 가집니다. 예를 들어 두 메서드의 호출 구문이 같고 익명 함수를 둘 다로 변환할 수 있으므로 메서드가 대리자 또는 식 트리를 요청할 수 있으므로 패턴의 메서드를 인스턴스 메서드 또는 확장 메서드로 구현할 수 있습니다. 일부 쿼리 식 패턴만 구현하는 형식은 형식이 지원하는 메서드에 매핑되는 쿼리 식 변환만 지원합니다. 끝 메모
참고:
System.Linq네임스페이스는System.Collections.Generic.IEnumerable<T>인터페이스를 구현하는 모든 형식에 대한 쿼리 식 패턴의 구현을 제공합니다. 끝 메모
12.23 대입 연산자
12.23.1 일반
할당 연산자 중 하나를 제외한 모든 항목은 변수, 속성, 이벤트 또는 인덱서 요소에 새 값을 할당합니다. 예외인 = ref참조(§9.5)를 참조 변수(§9.7)에 할당합니다.
assignment
: unary_expression assignment_operator expression
;
assignment_operator
: '=' 'ref'? | '+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' |
'<<=' | '??='
| right_shift_assignment
;
할당의 왼쪽 피연산자는 변수로 분류된 식이거나, = ref를 제외하고는 속성 액세스, 인덱서 액세스, 이벤트 액세스 또는 튜플이여야 합니다. 선언식은 왼쪽 피연산자로 직접 허용되지 않지만, 디콘스트럭팅 할당의 평가 단계에서 발생할 수 있습니다.
연산자 = ref은 참조 할당 연산자라고 불립니다. 왼쪽 피연산자가 지정한 참조 변수의 참조자가 되는 것이 오른쪽 피연산자로, 이는 variable_reference (§9.5)이어야 합니다. ref 대입 연산자는 §12.23.3에 설명되어 있습니다.
및 연산자가 = 아닌 할당 연산자를 = ref자라고 합니다. 이러한 연산자는 다음과 같이 처리됩니다.
-
??=연산자의 경우, 왼쪽 피연산자의 값이null인 경우에만 오른쪽 피연산자가 평가되어 왼쪽 피연산자가 지정한 변수, 속성 또는 인덱서 요소에 결과가 할당됩니다. - 그렇지 않으면 표시된 연산이 두 피연산자에 대해 수행된 다음 결과 값이 왼쪽 피연산자가 지정한 변수, 속성 또는 인덱서 요소에 할당됩니다. 복합 할당 연산자는 §12.23.4에 설명되어 있습니다.
+= 이벤트 액세스 식을 왼쪽 피연산자로 사용하는 연 -= 산자를 이벤트 할당 연산자라고 합니다. 이벤트 액세스를 왼쪽 피연산자로 사용할 때는 다른 할당 연산자를 사용할 수 없습니다. 이벤트 할당 연산자는 §12.23.5에 설명되어 있습니다.
할당 연산자는 오른쪽 연결 연산자입니다. 즉, 작업이 오른쪽에서 왼쪽으로 그룹화됩니다.
예제:
a = b = c형태의 식은a = (b = c)로 평가됩니다. 예제 종료
12.23.2 단순 할당
= 연산자를 단순 대입 연산자라고 합니다.
단순 할당의 왼쪽 피연산자가 E.P 또는 E[Ei] 형식이고 E가 컴파일 시간 형식 dynamic일 경우, 해당 할당은 동적으로 바인딩됩니다(§12.3.3). 이 경우에 할당 표현식의 컴파일 시 형식은 dynamic이며, 아래에 설명된 해결 방법은 E의 런타임 시 형식에 따라 런타임에 수행됩니다. 왼쪽 피연산자가 E[Ei]컴파일 시간 형식을 가진 Ei의 적어도 하나의 요소를 포함하는 dynamic 형식일 때, 그리고 E의 컴파일 시간 형식이 배열이 아닌 경우, 결과 인덱서 액세스는 제한된 컴파일 시간 검사(§12.6.5)를 통해 동적으로 바인딩됩니다.
간단한 할당에서 왼쪽 피연산자가 튜플로 분류되는 경우, 이를 분해 할당이라고도 합니다. 왼쪽 피연산자의 튜플 요소에 요소 이름이 있으면 컴파일 시간 오류가 발생합니다. 왼쪽 피연산자의 튜플 요소 중 하나가 declaration_expression이고, 다른 요소가 declaration_expression 또는 단순 삭제가 아닌 경우, 컴파일 시 오류가 발생합니다.
간단한 할당 유형 x = y는 x의 y 할당 유형으로, 이는 다음과 같이 재귀적으로 결정됩니다.
-
x이 튜플 식이며,(x1, ..., xn)이y요소((y1, ..., yn))가 있는 튜플 식으로 분해될 수 있고,n의 각 할당이 유형을 가지고 있다면, 그 할당은xi유형을 가집니다. - 그렇지 않으면
x이(가) 변수로 분류되고, 변수가readonly이(가) 아니며,x이(가)T형식이고,y이(가)T로 암시적 변환이 가능한 경우, 그러면 할당은T형식을 갖습니다. - 그렇지 않은 경우,
x가 암시적으로 형식화된 변수(즉, 암시적으로 형식화된 선언 식)로 분류되고,y이T형식을 가지는 경우, 유추된 변수의 형식은T이고, 할당은T형식을 갖습니다. - 그렇지 않으면,
x이 속성 또는 인덱서 액세스로 분류되고, 그 속성 또는 인덱서에 접근 가능한 설정 접근자가 있으며,x이T유형이고,y이T로의 암시적 변환이 있는 경우, 그 할당은T유형을 갖습니다. - 그렇지 않으면 할당이 유효하지 않고 바인딩 시간 오류가 발생합니다.
타입 x = y의 형식을 갖춘 T 형식의 단순 할당의 런타임 처리는 타입 x의 형식을 가지는 y을 T에 할당하는 것으로 수행되며, 다음과 같은 재귀적 단계들로 구성됩니다.
-
x는 이미 평가되지 않았다면 평가됩니다. -
x변수로 분류되는 경우y평가되고 필요한 경우 암시적 변환(T)을 통해 변환됩니다. -
x속성 또는 인덱서 액세스 권한으로 분류되는 경우: -
x가 차수(x1, ..., xn)의 튜플n로 분류되는 경우:-
y는n요소로e튜플 표현으로 분해됩니다. - 결과 튜플
t은 암시적 튜플 변환을 사용하여e을T로 변환함으로써 만들어집니다. - 왼쪽에서 오른쪽으로 순서대로 각
xi에 대해,xi가 다시 평가되지 않는다는 점을 제외하고t.Itemi를xi에 할당합니다. -
t할당의 결과로 생성됩니다.
-
참고:
x의 컴파일 시간 형식이dynamic이고,y의 컴파일 시간 형식에서dynamic으로의 암시적 변환이 있는 경우, 런타임 확인이 필요하지 않습니다. 끝 메모
참고: 배열 공변성 규칙(§17.6)은
A[]에서B[]으로의 암시적 참조 변환이 존재할 경우, 배열 형식B의 값이A배열 형식의 인스턴스에 대한 참조로 허용합니다. 이러한 규칙 때문에 reference_type 배열 요소에 할당하려면 할당되는 값이 배열 인스턴스와 호환되는지 확인하기 위한 런타임 검사가 필요합니다. 예제에서string[] sa = new string[10]; object[] oa = sa; oa[0] = null; // OK oa[1] = "Hello"; // OK oa[2] = new ArrayList(); // ArrayTypeMismatchException마지막 할당은
System.ArrayTypeMismatchException의 요소에ArrayList에 대한 참조를 저장할 수 없기 때문에string[]이(가) 발생합니다.끝 메모
struct_type 선언된 속성 또는 인덱서가 할당의 대상인 경우 속성 또는 인덱서 액세스와 연결된 인스턴스 식은 변수로 분류되어야 합니다. 인스턴스 식이 값으로 분류될 경우, 바인딩 시점에 오류가 발생합니다.
참고: §12.8.7때문에 필드에도 동일한 규칙이 적용됩니다. 끝 메모
예제: 주어진 선언에 대하여:
struct Point { int x, y; public Point(int x, int y) { this.x = x; this.y = y; } public int X { get { return x; } set { x = value; } } public int Y { get { return y; } set { y = value; } } } struct Rectangle { Point a, b; public Rectangle(Point a, Point b) { this.a = a; this.b = b; } public Point A { get { return a; } set { a = value; } } public Point B { get { return b; } set { b = value; } } }예제에서
Point p = new Point(); p.X = 100; p.Y = 100; Rectangle r = new Rectangle(); r.A = new Point(10, 10); r.B = p;
p.X및p.Y변수이므로r.A,r.B,p및r할당이 허용됩니다. 그러나 예제에서는Rectangle r = new Rectangle(); r.A.X = 10; r.A.Y = 10; r.B.X = 100; r.B.Y = 100;
r.A및r.B변수가 아니므로 할당은 모두 유효하지 않습니다.예제 종료
12.23.3 참조 할당
= ref 연산자는 참조 할당 연산자로 알려져 있습니다.
왼쪽 피연산자는 참조 변수(§9.7), 참조 매개 변수(this제외), 출력 매개 변수 또는 입력 매개 변수에 바인딩되는 식이어야 합니다. 오른쪽 피연산자는 왼쪽 피연산자의 값과 동일한 형식의 값을 참조하는 variable_reference를 제공하는 식이어야 합니다.
왼쪽 피연산자의 ref-safe-context(§9.7.2)가 오른쪽 피연산자의 ref-safe-context보다 넓으면 컴파일 시간 오류입니다.
오른쪽 피연산자는 ref 할당 시점에 확실히 할당되어야 합니다.
왼쪽 피연산자가 출력 매개 변수에 바인딩되는 경우 해당 출력 매개 변수가 ref 할당 연산자의 시작 부분에 확실히 할당되지 않은 경우 오류가 발생합니다.
왼쪽 피연산자가 쓰기 가능한 ref인 경우(즉, ref readonly 로컬 또는 입력 매개 변수가 아닌 다른 것을 지정할 때), 오른쪽 피연산자는 쓰기 가능한 variable_reference여야 합니다. 오른쪽 피연산자 변수를 쓸 수 있는 경우 왼쪽 피연산자는 쓰기 가능하거나 읽기 전용 참조일 수 있습니다.
이 연산은 왼쪽 피연산자를 오른쪽 피연산자 변수의 별칭으로 만듭니다. 오른쪽 피연산자 변수를 쓸 수 있는 경우에도 별칭을 읽기 전용으로 만들 수 있습니다.
ref 대입 연산자는 할당된 형식의 variable_reference 를 생성합니다. 왼쪽 피연산자를 쓸 수 있는 경우 쓸 수 있습니다.
ref 대입 연산자는 오른쪽 피연산자가 참조하는 스토리지 위치를 읽어서는 안 됩니다.
예제:
= ref사용하는 몇 가지 예는 다음과 같습니다.public static int M1() { ... } public static ref int M2() { ... } public static ref uint M2u() { ... } public static ref readonly int M3() { ... } public static void Test() { int v = 42; ref int r1 = ref v; // OK, r1 refers to v, which has value 42 r1 = ref M1(); // Error; M1 returns a value, not a reference r1 = ref M2(); // OK; makes an alias r1 = ref M2u(); // Error; lhs and rhs have different types r1 = ref M3(); // error; M3 returns a ref readonly, which r1 cannot honor ref readonly int r2 = ref v; // OK; make readonly alias to ref r2 = ref M2(); // OK; makes an alias, adding read-only protection r2 = ref M3(); // OK; makes an alias and honors the read-only r2 = ref (r1 = ref M2()); // OK; r1 is an alias to a writable variable, // r2 is an alias (with read-only access) to the same variable }예제 종료
참고:
= ref연산자를 사용하여 코드를 읽을 때ref부분을 피연산자의 일부라는 것으로 읽고 싶어질 수 있습니다. 피연산자가 조건부?:식일 때 특히 혼란스럽습니다. 예를 들어ref int a = ref b ? ref x : ref y;을 읽을 때는= ref이 연산자이며b ? ref x : ref y가 오른쪽 피연산자인ref int a = ref (b ? ref x : ref y);으로 읽는 것이 중요합니다. 중요한 것은, 언뜻 보기에ref b식이 해당 문의 일부로 보일 수 있지만, 실제로는 부분이 아니라는 점입니다. 끝 메모
12.23.4 복합 할당
복합 할당의 왼쪽 피연산자가 E.P 또는 E[Ei]의 형식이고 E의 컴파일 시간 형식이 dynamic일 경우, 할당은 동적으로 바인딩됩니다(§12.3.3). 이 경우에 할당 표현식의 컴파일 시 형식은 dynamic이며, 아래에 설명된 해결 방법은 E의 런타임 시 형식에 따라 런타임에 수행됩니다. 왼쪽 피연산자가 E[Ei]컴파일 시간 형식을 가진 Ei의 적어도 하나의 요소를 포함하는 dynamic 형식일 때, 그리고 E의 컴파일 시간 형식이 배열이 아닌 경우, 결과 인덱서 액세스는 제한된 컴파일 시간 검사(§12.6.5)를 통해 동적으로 바인딩됩니다.
a ??= b는 (T) (a ?? (a = b))와 동등하지만, a는 한 번만 평가됩니다. T의 형식이 동적일 때 a의 형식은 b이며, 그렇지 않으면 T의 형식은 a ?? b입니다.
그렇지 않으면, x «op»= y 형식으로 된 작업은 이진 연산자 오버로드 확인(§12.4.5)을 적용하여 x «op» y로 작성된 것처럼 처리됩니다. 그러면
- 선택한 연산자의 반환 유형이 암시적으로
x유형으로 변환될 수 있는 경우,x = x «op» y가 한 번만 평가된다는 점을 제외하고 연산은x로 평가됩니다. - 선택한 연산자가 미리 정의된 연산자이고 선택한 연산자의 반환 형식이
x형식으로 명시적으로 변환할 수 있으며,y이x형식으로 암시적으로 변환할 수 있거나 연산자가 시프트 연산자인 경우, 연산은x = (T)(x «op» y)의 형식인T로 평가됩니다. 단,x은 한 번만 평가됩니다. - 그렇지 않으면 복합 할당이 유효하지 않으며 바인딩 시간 오류가 발생합니다.
"한 번만 평가됨"이라는 용어는 x «op» y평가에서 x 구성 식의 결과가 일시적으로 저장되고 x할당을 수행할 때 다시 사용됨을 의미합니다.
예제:
A()[B()] += C()A반환하는 메서드이고int[]및BC반환하는 메서드인 할당int메서드에서 메서드는A,B,C순서로 한 번만 호출됩니다. 예제 종료
복합 대입 연산의 왼쪽 피연산자가 속성 접근 또는 인덱서 접근인 경우, 해당 속성이나 인덱서에는 get 접근자와 set 접근자가 모두 있어야 합니다. 그렇지 않으면 바인딩 시간 오류가 발생합니다.
위의 두 번째 규칙은 특정 맥락에서 x «op»= y이 x = (T)(x «op» y)로 평가되도록 허용합니다. 규칙은 왼쪽 피연산자가 sbyte, byte, short, ushort또는 char형식일 때 미리 정의된 연산자를 복합 연산자로 사용할 수 있도록 존재합니다. 두 인수가 모두 이러한 형식 중 하나인 경우에도 미리 정의된 연산자는 int설명한 대로 형식의 결과를 생성합니다. 캐스트가 없으면 결과를 왼쪽 피연산자에 할당할 수 없게 됩니다.
미리 정의된 연산자 규칙의 단순한 직관적인 효과는 x «op»= y 및 x «op» y 모두 허용되는 경우 x = y이 허용된다는 것입니다.
예제: 다음 코드에서
byte b = 0; char ch = '\0'; int i = 0; b += 1; // OK b += 1000; // Error, b = 1000 not permitted b += i; // Error, b = i not permitted b += (byte)i; // OK ch += 1; // Error, ch = 1 not permitted ch += (char)1; // OK각 오류에 대한 직관적인 이유는 해당 단순 할당도 오류였을 것이기 때문입니다.
예제 종료
참고: 이는 복합 대입 연산이 리프트 연산자를 지원한다는 것을 의미합니다. 복합 할당
x «op»= y는x = x «op» y또는x = (T)(x «op» y)중 하나로 평가되므로, 평가 규칙은 리프팅된 연산자를 암시적으로 다룹니다. 끝 메모
12.23.5 이벤트 할당
a += or -= 연산자의 왼쪽 피연산자가 이벤트 액세스로 분류되면 식은 다음과 같이 평가됩니다.
- 이벤트 액세스의 인스턴스 식(있는 경우)이 평가됩니다.
-
+=또는-=연산자의 오른쪽 피연산자가 계산되고 필요한 경우 암시적 변환을 통해 왼쪽 피연산자의 형식으로 변환됩니다(§10.2). - 이전 단계에서 계산된 값으로 구성된 인수 목록을 사용하여 이벤트의 이벤트 접근자가 호출됩니다. 연산자가
+=일 경우에는 add 접근자 메서드가 호출되고, 연산자가-=일 경우에는 제거 접근자 메서드가 호출됩니다.
이벤트 할당 식은 값을 생성하지 않습니다. 따라서 이벤트 할당 식은 statement_expression 컨텍스트에서만 유효합니다(§13.7).
12.24 식
식 은 non_assignment_expression 이거나 할당입니다.
expression
: non_assignment_expression
| assignment
;
non_assignment_expression
: declaration_expression
| conditional_expression
| lambda_expression
| query_expression
;
12.25 상수 식
상수 식은 컴파일 시간에 완전히 평가되어야 하는 식입니다.
constant_expression
: expression
;
상수 식에는 null 값 또는 다음 형식 중 하나가 있어야 합니다.
-
sbyte,byte,short,ushort,int,uint, ,longulong,char,floatdoubledecimalboolstring, ; - 열거형 또는
- 참조 타입의 기본값 표현식(§12.8.21)입니다.
상수 식에서는 다음 구문만 허용됩니다.
- 리터럴(
null포함) -
const클래스, 구조체 및 인터페이스 형식의 멤버에 대한 참조입니다. - 열거형 형식의 멤버에 대한 참조입니다.
- 로컬 상수에 대한 참조입니다.
- 괄호로 묶인 하위 식으로, 이들 자체가 상수 식입니다.
- 캐스트 표현
- 표현식
checked및unchecked. -
nameof수식입니다. - 미리 정의된
+,-,!(논리 부정) 및~단항 연산자입니다. - 미리 정의된
+,-,*,/,%,<<,>>,&,|,^,&&,||,==,!=,<,>,<=및>=이진 연산자입니다. -
?:조건부 연산자입니다. -
!null 용서 연산자(§12.8.9)입니다. -
sizeof식은 관리되지 않는 형식이 상수 값을 반환하는 §24.6.9 에sizeof지정된 형식 중 하나일 경우 - 형식이 위에 나열된 형식 중 하나인 경우 기본값의 표현식입니다.
다음 변환은 상수 식에서 허용됩니다.
- ID 변환
- 숫자 변환
- 열거형 변환
- 상수 식 변환
- 변환의 원본이
null값으로 계산되는 상수 식인 경우에, 참조 변환은 암시적이거나 명시적일 수 있습니다.
참고: 비
null값의 boxing, unboxing 및 암시적 참조 변환을 비롯한 다른 변환은 상수 식에서 허용되지 않습니다. 끝 메모
예제: 다음 코드에서
class C { const object i = 5; // error: boxing conversion not permitted const object str = "hello"; // error: implicit reference conversion }박싱 변환이 필요하기 때문에
i의 초기화는 오류입니다.str초기화는 비null값으로부터 암시적 참조 변환이 필요하기 때문에 오류입니다.예제 종료
식이 위에 나열된 요구 사항을 충족할 때마다 컴파일 시에 평가됩니다. 식이 비 상수 구문을 포함하는 더 큰 식의 하위 식인 경우에도 마찬가지입니다.
상수 식의 컴파일 타임 평가에서는 비상수 식의 런타임 평가와 동일한 규칙을 사용합니다. 다만, 런타임 평가에서 예외가 발생하는 경우에는, 컴파일 타임 평가 시 컴파일 타임 오류가 발생한다는 점이 다릅니다.
상수 식이 unchecked 컨텍스트에 명시적으로 배치되지 않는 한, 식의 컴파일 시간 평가 중에 정수 형식 산술 연산 및 변환에서 발생하는 오버플로는 항상 컴파일 시간 오류를 발생합니다(§12.8.20).
상수 식은 아래에 나열된 컨텍스트에서 필요하며 constant_expression사용하여 문법에 표시됩니다. 이러한 컨텍스트에서는 컴파일 시간에 식을 완전히 평가할 수 없는 경우 컴파일 시간 오류가 발생합니다.
- 상수 선언(§15.4)
- 열거형 멤버 선언(§20.4)
- 매개 변수 목록의 기본 인수(§15.6.2)
-
case문장의switch레이블(§13.8.3). -
goto case진술 (§13.10.4) - 이니셜라이저를 포함하는 배열 생성 식(§12.8.17.4)의 차원 길이입니다.
- 특성(§23)
- constant_pattern에서 (§11.2.3)
암시적 상수 식 변환(§10.2.11)을 사용하면 상수 식의 값이 대상 형식 범위 내에 있는 경우 형식 int 상수 식을 sbyte, byte, short, ushort, uint또는 ulong변환할 수 있습니다.
12.26 부울 식
boolean_expression은 bool유형의 결과를 생성하는 식입니다. 이는 직접 또는 다음에 명시된 특정 컨텍스트에서 operator true을 적용하여 수행될 수 있습니다.
boolean_expression
: expression
;
if_statement(§13.8.2), while_statement(§13.9.2), do_statement(§13.9.3), 또는 for_statement(§13.9.4)의 제어 조건식은 boolean_expression입니다. 연산자(?:)의 제어 조건식은 boolean_expression 동일한 규칙을 따르지만 연산자 우선 순위의 이유로 null_coalescing_expression 분류됩니다.
다음과 같이 형식의 값을 생성할 수 있으려면 Ebool이 필요합니다.
- E가 암시적으로
bool변환할 수 있는 경우 런타임에 암시적 변환이 적용됩니다. - 그렇지 않으면 단항 연산자 오버로드 해석(§12.4.4)을 사용하여
operator true에서E의 고유한 최적의 구현을 찾고, 해당 구현은 런타임에 적용됩니다. - 이러한 연산자를 찾을 수 없으면 바인딩 시간 오류가 발생합니다.
ECMA C# draft specification