재귀 패턴 일치
메모
이 문서는 기능 사양입니다. 사양은 기능의 디자인 문서 역할을 합니다. 여기에는 기능 디자인 및 개발 중에 필요한 정보와 함께 제안된 사양 변경 내용이 포함됩니다. 이러한 문서는 제안된 사양 변경이 완료되고 현재 ECMA 사양에 통합될 때까지 게시됩니다.
기능 사양과 완료된 구현 간에 약간의 불일치가 있을 수 있습니다. 이러한 차이는 관련된 LDM(언어 디자인 모임) 노트 에 기록됩니다.
사양문서에서 기능 사양을 C# 언어 표준으로 채택하는 프로세스를 자세히 확인할 수 있습니다.
요약
C#용 패턴 일치 확장을 사용하면 기능 언어에서 대수 데이터 형식 및 패턴 일치의 많은 이점을 얻을 수 있지만 기본 언어의 느낌과 원활하게 통합됩니다. 이 접근 방식의 요소는 프로그래밍 언어 F#의 관련 기능, 경량 언어를 통한 확장 가능한 패턴 일치, 패턴과 일치하는 개체 Scala에서 영감을 받았습니다.
상세 디자인
Is 표현식
is
연산자는 패턴대해 식을 테스트하도록 확장됩니다.
relational_expression
: is_pattern_expression
;
is_pattern_expression
: relational_expression 'is' pattern
;
이 형식의 relational_expression C# 사양의 기존 양식에 추가됩니다.
is
토큰의 왼쪽에 있는 relational_expression 값을 지정하지 않거나 형식이 없는 경우 컴파일 시간 오류입니다.
패턴의 모든
참고:
의 형식과 constant_pattern 간에는 기술적인 모호성이 있으며, 둘 중 하나가 정규화된 식별자의 올바른 구문 분석이 될 수 있습니다. 이전 버전의 언어와의 호환성을 위해 형식으로 바인딩하려고 시도합니다. 그것이 실패할 경우에만 다른 컨텍스트에서 식을 해결할 때처럼 첫 번째로 발견되는 항목(반드시 상수 또는 형식이어야 함)으로 해결합니다. 이 모호성은is
식의 우변에만 존재합니다.
패턴
패턴은 is_pattern 연산자, switch_statement, 및 switch_expression에서 사용되며, 입력 값이라 부르는 들어오는 데이터를 비교할 데이터의 형태를 표현합니다. 데이터의 일부가 하위 패턴과 일치할 수 있도록 패턴이 재귀적일 수 있습니다.
pattern
: declaration_pattern
| constant_pattern
| var_pattern
| positional_pattern
| property_pattern
| discard_pattern
;
declaration_pattern
: type simple_designation
;
constant_pattern
: constant_expression
;
var_pattern
: 'var' designation
;
positional_pattern
: type? '(' subpatterns? ')' property_subpattern? simple_designation?
;
subpatterns
: subpattern
| subpattern ',' subpatterns
;
subpattern
: pattern
| identifier ':' pattern
;
property_subpattern
: '{' '}'
| '{' subpatterns ','? '}'
;
property_pattern
: type? property_subpattern simple_designation?
;
simple_designation
: single_variable_designation
| discard_designation
;
discard_pattern
: '_'
;
선언 패턴
declaration_pattern
: type simple_designation
;
declaration_pattern 표현식이 지정된 형식인지 테스트하고, 테스트가 성공하면 해당 형식으로 형 변환합니다. 지정이 single_variable_designation경우, 주어진 식별자가 명명하는 주어진 형식의 지역 변수를 도입할 수 있습니다. 해당 지역 변수는 패턴 일치 작업의 결과가 true
일 때 확실히 할당됩니다.
이 식의 런타임 의미 체계는 패턴의 null
이 아니면, is operator
의 결과는 true
입니다.
왼쪽의 정적 형식과 지정된 형식의 특정 조합은 호환되지 않는 것으로 간주되어 컴파일 시간 오류가 발생합니다. 정적 형식 E
값은 id 변환, 암시적 참조 변환, boxing 변환, 명시적 참조 변환, 언박싱 변환 중 하나가 E
에서 T
로 존재하거나, 이러한 형식 중 하나가 열린 형식인 경우에 형식 T
과(와) 패턴 호환 있다고 합니다. 입력 형식 E
이 일치하는 형식 패턴의 형식과(와) 패턴 호환이(가) 아닐 경우, 컴파일 시 오류가 발생합니다.
형식 패턴은 참조 형식의 런타임 형식 테스트를 수행하는 데 유용하며 관용구를 대체합니다.
var v = expr as Type;
if (v != null) { // code using v
약간 더 간결한
if (expr is Type v) { // code using v
형식이 null 허용 값 형식인 경우 오류가 발생합니다.
형식 패턴은 nullable 형식의 값을 테스트하는 데 사용할 수 있습니다. Nullable<T>
형식의 값(또는 박싱된 T
)이 null이 아니고 T2
의 형식이 T
, 또는 T
의 일부 기본 형식이나 인터페이스인 경우에 형식 패턴 T2 id
과 일치합니다. 예를 들어 코드 조각에서
int? x = 3;
if (x is int v) { // code using v
if
문의 조건은 런타임에 true
이며, 변수 v
는 블록 내에서 int
형식의 값 3
을 보유합니다. 블록 후 변수 v
범위에 있지만 확실히 할당되지는 않습니다.
상수 패턴
constant_pattern
: constant_expression
;
상수 패턴은 상수 값에 대해 식의 값을 테스트합니다. 상수는 리터럴, 선언된 const
변수의 이름, 또는 열거형 상수와 같은 상수 표현식일 수 있습니다. 입력 값이 열린 형식이 아닌 경우 상수 식은 일치하는 식의 형식으로 암시적으로 변환됩니다. 입력 값의 형식이 상수 식의 형식과 패턴 호환
c 패턴은 object.Equals(c, e)
가 true
를 반환하는 경우, 변환된 입력값 와이 일치하는 것으로 간주됩니다.
사용자 정의 operator==
호출할 수 없으므로 e is null
새로 작성된 코드에서 null
테스트하는 가장 일반적인 방법으로 간주될 것으로 예상됩니다.
Var 패턴
var_pattern
: 'var' designation
;
designation
: simple_designation
| tuple_designation
;
simple_designation
: single_variable_designation
| discard_designation
;
single_variable_designation
: identifier
;
discard_designation
: _
;
tuple_designation
: '(' designations? ')'
;
designations
: designation
| designations ',' designation
;
지정이 simple_designation인 경우에는 표현식 e이 패턴과 일치합니다. 즉, var 패턴 일치는 항상 simple_designation성공합니다. "이 경우 simple_designation이 single_variable_designation이라면, e의 값은 새로 도입된 지역 변수에 바인딩됩니다." 지역 변수의 형식은 e정적 형식입니다.
지정이 tuple_designation인 경우, 패턴은 (var
지정, ... )
양식의 positional_pattern과 동일합니다. 여기서 지정은 tuple_designation내에 있는 것입니다. 예를 들어 패턴 var (x, (y, z))
(var x, (var y, var z))
동일합니다.
이름이 var
형식에 바인딩되는 경우 오류가 발생합니다.
폐기 패턴
discard_pattern
: '_'
;
과 e로 이루어진 식은 항상 패턴 _
에 일치합니다. 즉, 모든 표현식은 버리는 패턴과 일치합니다.
무시 패턴은 is_pattern_expression패턴으로 사용할 수 없습니다.
위치 패턴
위치 패턴은 입력 값이 null
않는지 확인하고, 적절한 Deconstruct
메서드를 호출하고, 결과 값에 대한 추가 패턴 일치를 수행합니다. 입력 값의 형식이 Deconstruct
을 포함한 형식과 같거나, 입력 값의 형식이 튜플 형식이거나, 입력 값의 형식이 object
또는 ITuple
이고 식의 런타임 형식이 ITuple
을 구현하는 경우, 튜플 유사 패턴 구문도 지원합니다.
positional_pattern
: type? '(' subpatterns? ')' property_subpattern? simple_designation?
;
subpatterns
: subpattern
| subpattern ',' subpatterns
;
subpattern
: pattern
| identifier ':' pattern
;
형식 생략하면 입력 값의 정적 형식으로 사용합니다.
입력 값이 패턴 형식(
subpattern_list)
와 일치할 경우, 형식 에서 찾아 액세스 가능한 선언 Deconstruct
중 하나를 선택하는데, 이때 분해 선언과 동일한 규칙을 사용하여 메서드를 결정합니다.
positional_pattern 형식을 생략하고, 식별자가 없는 단일 하위 패턴만 있으며, property_subpattern이 없고, simple_designation이 없는 경우에는 오류입니다. 이는 괄호로 둘러싸인 constant_pattern과 positional_pattern를 구분합니다.
목록의 패턴과 일치하는 값을 추출하려면
- 유형이 생략되고 입력 값의 유형이 튜플 유형인 경우, 하위 패턴의 수는 튜플의 기수와 같아야 합니다. 각 튜플 요소는 해당 에 있는하위 패턴과 일치하며, 모든 요소가 성공하면 일치가 성공합니다. 하위 패턴에 식별자 가 있는 경우, 해당 위치의 튜플 형식에서 튜플 요소의 이름을 반드시 지정해야 합니다.
- 그렇지 않으면, 만약 적절한
이 형식의 멤버로 존재한다면, 입력 값의 형식이형식의 와 패턴 호환않은 경우 컴파일 시간 오류가 발생합니다. 런타임에 입력 값은 형식에 대해 테스트됩니다. 실패하면 위치 패턴 일치가 실패합니다. 성공하면 입력 값이 이 형식으로 변환되고 Deconstruct
새 컴파일러 생성 변수를 사용하여 호출되어out
매개 변수를 받습니다. 수신된 각 값은 해당 하위 패턴과 비교하여 일치하며, 이러한 모든 비교가 성공하면 전체 일치가 성공합니다. 하위에 식별자가 있는 경우, 해당Deconstruct
의 위치에 있는 매개변수의 이름으로 지정해야 합니다. - 그렇지 않은 경우, 타입의이 생략되고 입력 값이
object
또는ITuple
타입이거나 암시적 참조 변환으로ITuple
로 변환될 수 있는 타입이며, 하위 패턴 중에 식별자이 나타나지 않으면,ITuple
를 사용하여 일치시킵니다. - 그렇지 않으면 패턴이 컴파일 시간 오류입니다.
런타임에 하위 패턴이 매칭되는 순서는 지정되지 않으며, 일치가 실패할 경우 모든 하위 패턴과 일치하려고 시도하지 않을 수 있습니다.
예
이 예제에서는 이 사양에 설명된 많은 기능을 사용합니다.
var newState = (GetState(), action, hasKey) switch {
(DoorState.Closed, Action.Open, _) => DoorState.Opened,
(DoorState.Opened, Action.Close, _) => DoorState.Closed,
(DoorState.Closed, Action.Lock, true) => DoorState.Locked,
(DoorState.Locked, Action.Unlock, true) => DoorState.Closed,
(var state, _, _) => state };
속성 패턴
속성 패턴은 입력 값이 null
않는지 확인하고 액세스 가능한 속성 또는 필드를 사용하여 추출된 값과 재귀적으로 일치합니다.
property_pattern
: type? property_subpattern simple_designation?
;
property_subpattern
: '{' '}'
| '{' subpatterns ','? '}'
;
property_pattern의 하위가 식별자를 포함하지 않으면 오류가 발생합니다. 이 경우 두 번째 형식으로, 식별자을 포함해야 합니다. 마지막 하위 패턴 뒤의 후행 쉼표는 선택 사항입니다.
null 검사 패턴은 사소한 속성 패턴에서 제외됩니다. 문자열 s
null이 아닌지 확인하려면 다음 양식을 작성할 수 있습니다.
if (s is object o) ... // o is of type object
if (s is string x) ... // x is of type string
if (s is {} x) ... // x is of type string
if (s is {}) ...
패턴
런타임 시, 식이 T와 테스트됩니다. 실패할 경우 속성 패턴 매칭이 실패하여 결과는 false
가 됩니다. 성공하면 각 property_subpattern 필드 또는 속성이 읽혀지고 해당 값이 해당 패턴과 일치합니다. 전체 경기의 결과는 이들 중 하나의 결과가 false
일 때에만 false
입니다. 서브패턴이 일치하는 순서는 지정되지 않으며, 실패한 일치가 런타임 시 모든 하위 페이턴과 일치하지 않을 수 있습니다. 일치가 성공하고 property_pattern의 simple_designation이 single_variable_designation인 경우, 일치하는 값이 할당된 T 형식의 변수를 정의합니다.
참고: 속성 패턴을 사용하여 무명 형식과 패턴 일치시킬 수 있습니다.
본보기
if (o is string { Length: 5 } s)
스위치 표현식
식 컨텍스트에서 switch
와 같은 의미 체계를 지원하기 위해 switch_expression이 추가됩니다.
C# 언어 구문은 다음 구문 프로덕션으로 보강됩니다.
multiplicative_expression
: switch_expression
| multiplicative_expression '*' switch_expression
| multiplicative_expression '/' switch_expression
| multiplicative_expression '%' switch_expression
;
switch_expression
: range_expression 'switch' '{' '}'
| range_expression 'switch' '{' switch_expression_arms ','? '}'
;
switch_expression_arms
: switch_expression_arm
| switch_expression_arms ',' switch_expression_arm
;
switch_expression_arm
: pattern case_guard? '=>' expression
;
case_guard
: 'when' null_coalescing_expression
;
switch_expression은 expression_statement으로서 허용되지 않습니다.
우리는 이것을 향후 개정에서 완화하는 것을 고려하고 있습니다.
switch_expression의 형식은 switch_expression_arm의 =>
토큰 오른쪽에 나타나는 식의 가장 일반적인 형식(§12.6.3.15)입니다. 이러한 형식이 존재하며 스위치 표현식 내부 각각의 암의 식이 해당 형식으로 암묵적으로 변환될 수 있는 경우입니다. 또한 각 arm의 식에서 T
으로의 암시적 변환이 가능한 모든 형식 T
로 변환하기 위한, 미리 정의된 암시적 변환인 새 스위치 식 변환을 추가합니다.
이전 패턴과 가드가 항상 일치하여 일부 switch_expression_arm패턴이 결과에 영향을 미칠 수 없는 경우, 이는 오류입니다.
switch 식의 일부 가지가 입력의 모든 값을 처리하는 경우, switch 식은 완전한 라고 합니다. 스위치 식이 전체
런타임에 switch_expression의 결과는 switch_expression의 왼쪽에 있는 식이 switch_expression_arm의 패턴과 일치하는 첫 번째 switch_expression_arm의 식의 값이며, switch_expression_arm에 case_guard가 있는 경우, 해당 조건이 true
로 평가되는 것입니다. 이러한 switch_expression_arm이 없을 경우, switch_expression 는 예외 System.Runtime.CompilerServices.SwitchExpressionException
의 인스턴스를 발생시킵니다.
튜플 리터럴에서 전환할 때 선택적인 괄호 사용
switch_statement사용하여 튜플 리터럴을 켜려면 중복 파렌으로 보이는 항목을 작성해야 합니다.
switch ((a, b))
{
허용하려면
switch (a, b)
{
switch 문의 괄호는 전환되는 식이 튜플 리터럴인 경우 선택 사항입니다.
패턴 일치의 평가 순서
패턴 일치 중에 실행되는 작업의 순서를 변경할 수 있는 컴파일러 유연성을 제공하면 패턴 일치의 효율성을 개선하는 데 사용할 수 있는 유연성을 허용할 수 있습니다. (강제되지 않은) 요구 사항은 패턴에서 액세스하는 속성과 분해 메서드가 "순수"(부작용 없는, idempotent 등)이어야 한다는 것입니다. 그렇다고 해서 순도를 언어 개념으로 추가하는 것은 아니라 컴파일러가 작업 순서를 다시 정렬할 수 있도록 허용하는 것만을 의미합니다.
Resolution 2018-04-04 LDM: 확인됨: 컴파일러는 Deconstruct
호출, 속성 액세스 및 ITuple
메서드 호출을 다시 정렬할 수 있으며 반환된 값이 여러 호출에서 동일하다고 가정할 수 있습니다. 컴파일러는 결과에 영향을 줄 수 없는 함수를 호출해서는 안 되며, 나중에 컴파일러에서 생성된 평가 순서를 변경하기 전에 매우 주의해야 합니다.
몇 가지 가능한 최적화
패턴 일치의 컴파일은 패턴의 일반적인 부분을 활용할 수 있습니다. 예를 들어 switch_statement 두 개의 연속 패턴에 대한 최상위 형식 테스트가 동일한 형식인 경우 생성된 코드는 두 번째 패턴에 대한 형식 테스트를 건너뛸 수 있습니다.
일부 패턴이 정수 또는 문자열인 경우 컴파일러는 이전 버전의 언어에서 switch 문에 대해 생성하는 것과 동일한 종류의 코드를 생성할 수 있습니다.
이러한 종류의 최적화에 대한 자세한 내용은 [스콧과 램지 (2000)].
C# feature specifications