다음을 통해 공유


재귀 패턴 일치

메모

이 문서는 기능 사양입니다. 사양은 기능의 디자인 문서 역할을 합니다. 여기에는 기능 디자인 및 개발 중에 필요한 정보와 함께 제안된 사양 변경 내용이 포함됩니다. 이러한 문서는 제안된 사양 변경이 완료되고 현재 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 값을 지정하지 않거나 형식이 없는 경우 컴파일 시간 오류입니다.

패턴의 모든 식별자 연산자가 후 확실히 할당되는 새 지역 변수를 도입합니다(즉, true때 확실히 할당됨).

참고: 형식과 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일 때 확실히 할당됩니다.

이 식의 런타임 의미 체계는 패턴의 형식에 대해 왼쪽 relational_expression 피연산자의 런타임 형식을 테스트한다는 것입니다. 해당 런타임 유형(또는 일부 하위 유형)이며 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_designationsingle_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_patternpositional_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 {}) ...

패턴 형식property_pattern_list 식이 일치하면 식이 형식지정된 T 형식과 패턴 호환 않은 경우 컴파일 시간 오류입니다. 형식이 없는 경우 e정적 형식으로 간주합니다. 식별자이 있으면, 형식의패턴 변수를 선언합니다. property_pattern_list 왼편에 나타나는 각 식별자는 접근 가능한 읽기 속성 또는 T필드를 지정해야 합니다. property_patternsimple_designation 이 있다면, 이는 T유형의 패턴 변수를 정의합니다.

런타임 시, 식이 T와 테스트됩니다. 실패할 경우 속성 패턴 매칭이 실패하여 결과는 false가 됩니다. 성공하면 각 property_subpattern 필드 또는 속성이 읽혀지고 해당 값이 해당 패턴과 일치합니다. 전체 경기의 결과는 이들 중 하나의 결과가 false일 때에만 false입니다. 서브패턴이 일치하는 순서는 지정되지 않으며, 실패한 일치가 런타임 시 모든 하위 페이턴과 일치하지 않을 수 있습니다. 일치가 성공하고 property_patternsimple_designationsingle_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_expressionexpression_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_armcase_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)].