Quick Reference
pattern matching은 is, switch statement, switch expression에서 value의 type·constant·range·property shape를 검사하는 syntax입니다. pattern이 성공할 때만 variable을 도입하므로 cast와 null guard를 한 조건에 묶을 수 있습니다.
bool isReady = value is true; // constant
bool valid = score is >= 0 and <= 100; // relational + logical
if (input is string text and { Length: > 0 }) // type + property
{
Console.WriteLine(text);
}
if (candidate is null) { }constant,declaration/type,relational,property,var, discard_가 기본 surface입니다.- property pattern은 target이 null이면 match하지 않으므로 nested null guard를 줄일 수 있습니다.
is null은 overloaded==를 호출하지 않습니다.when은 switch arm에 추가 조건을 붙이는 guard이고, list·slice·복잡한 logical pattern은 고급 pattern에서 다룹니다.
Pattern 형태
object? value = GetValue();
bool constant = value is "start"; // constant pattern
bool typed = value is int number; // declaration/type pattern
bool inRange = number is >= 0 and < 10; // relational + logical pattern
bool shaped = user is { Address.City: "Seoul" }; // extended property pattern
bool any = value is var captured; // 항상 match, value를 captured에 둠type/declaration pattern은 null을 match하지 않습니다. property pattern도 target과 접근 경로가 null이면 match하지 않으며, match 성공 branch에서는 capture한 variable의 nullable state가 좁혀집니다.
if (order is { Customer: { IsPremium: true }, Total: > 100_000m })
{
ApplyPremiumDiscount(order);
}var pattern은 항상 성공하므로 switch의 마지막 arm에서 _ discard와 혼동하지 않습니다. _는 값을 버리는 discard pattern이고, var _는 이름 없는 variable을 capture하는 구문으로 보일 수 있어 보통 discard를 선택합니다.
Logical pattern과 guard
string Label(object? input) => input switch
{
null => "missing",
int number when number % 2 == 0 => "even",
int number and >= 0 => "non-negative odd",
int => "negative odd",
_ => "other",
};and, or, not은 pattern을 조합합니다. precedence는 not, and, or 순서이므로 복합 조건은 parentheses로 의도를 표시합니다. subpattern의 검사 순서가 side effect를 보장하는 API는 아니므로 property getter나 custom pattern 대상에 side effect를 두지 않습니다.
when은 pattern이 match한 뒤 일반 boolean expression으로 추가 판단할 때 씁니다. type/range/shape 자체를 pattern으로 읽을 수 있다면 when보다 pattern arm에 두고, database call·state mutation처럼 비용이 큰 guard는 arm selection 과정에 숨기지 않습니다.
Arm 순서와 선택
switch arm은 위에서 아래로 적용되며 더 넓은 pattern을 먼저 두면 뒤 arm이 도달 불가능해질 수 있습니다. compiler는 명백하게 subsumed된 arm을 error로 막습니다.
string Grade(int score) => score switch
{
>= 90 => "A",
>= 80 => "B",
>= 0 => "F",
_ => throw new ArgumentOutOfRangeException(nameof(score)),
};null, invalid range, future enum value 같은 input을 _로 조용히 숨길지, named fallback으로 보일지, exception으로 드러낼지 domain policy를 먼저 정합니다. 값 생산 switch의 complete/failure 결과는 switch expression에서 다룹니다.
자주 틀리는 부분
property pattern이 null exception을 던지지 않는다고 property getter가 항상 cheap·pure하다는 뜻은 아닙니다. observable getter, lazy loading, exception을 던지는 getter를 pattern에 넣으면 조건처럼 보이는 code에 side effect가 숨어듭니다.
pattern의 type test와 equality는 같은 연산이 아닙니다. null 검사에는 is null, domain equality에는 type이 정의한 comparer·Equals contract를 사용하세요.
참고 링크
2 sources