[C#] switch 식과 패턴 매칭 : 조건 분기를 간결하게

[C#] switch 식과 패턴 매칭 : 조건 분기를 간결하게

안녕하세요. 이번 글에서는 C#의 switch 식(switch expression)과 패턴 매칭(pattern matching)에 대해 알아보겠습니다.

기존 switch 문은 case마다 break를 쓰고 결과를 변수에 대입해야 해서 값을 고르는 용도로는 코드가 길어졌습니다. C# 8.0부터 값을 반환하는 switch 식이 추가되었고, 이후 버전에서 패턴이 계속 확장되어 조건 분기를 훨씬 간결하게 쓸 수 있습니다.

switch 문 vs switch 식

// before : switch 문
string name;
switch (day)
{
    case 0: name = "Sun"; break;
    case 6: name = "Sat"; break;
    default: name = "Weekday"; break;
}

// after : switch 식 (C# 8.0)
string name2 = day switch
{
    0 => "Sun",
    6 => "Sat",
    _ => "Weekday",
};

switch 식은 값 => 결과 형태로 쓰고, 나머지를 모두 받는 _ (discard)가 default 역할을 합니다. 어떤 입력도 처리하지 못하면 컴파일러가 경고하고, 실행 중에는 SwitchExpressionException이 발생합니다.

관계 패턴, 논리 패턴 (C# 9.0)

<, >, <=, >=로 범위를 비교하고, and, or, not으로 조합할 수 있습니다.

static string Grade(int score) => score switch
{
    >= 90 => "A",
    >= 80 => "B",
    >= 70 => "C",
    < 0 or > 100 => "Invalid",
    _ => "F",
};

Console.WriteLine(Grade(85));   // B
Console.WriteLine(Grade(120));  // Invalid

위에서부터 차례로 검사하므로 순서가 중요합니다. 앞의 패턴이 뒤의 패턴을 모두 포함하면 컴파일 오류가 납니다.

형식 패턴과 속성 패턴

형식(type) 패턴은 객체의 실제 타입으로 분기하며, 속성 패턴은 { 속성: 값 } 형태로 속성 값을 검사합니다.

public record Circle(double Radius);
public record Rect(double Width, double Height);

static double Area(object shape) => shape switch
{
    Circle c => Math.PI * c.Radius * c.Radius,
    Rect { Width: var w, Height: var h } => w * h,
    null => 0,
    _ => throw new ArgumentException("알 수 없는 도형"),
};

속성 패턴은 값을 추출하지 않고 조건만 검사할 수도 있습니다.

static string Check(Rect r) => r switch
{
    { Width: 0 } or { Height: 0 } => "Empty",
    { Width: var w, Height: var h } when w == h => "Square",
    _ => "Rectangle",
};

튜플 패턴

두 값 이상을 묶어 한 번에 비교할 때는 튜플 패턴이 편리합니다.

static string Rps(string a, string b) => (a, b) switch
{
    ("rock", "scissors") or ("scissors", "paper") or ("paper", "rock") => "A win",
    ("rock", "paper") or ("scissors", "rock") or ("paper", "scissors") => "B win",
    _ => "Draw",
};

is 식과 패턴

switch가 아니더라도 is 식에서 같은 패턴을 쓸 수 있습니다. 타입 검사와 변환을 한 번에 하고, not 패턴으로 null 검사도 간결해집니다.

if (obj is string s && s.Length > 0)
    Console.WriteLine(s.ToUpper());

if (obj is not null)
    Console.WriteLine("값이 있습니다.");

if (age is >= 7 and <= 19)
    Console.WriteLine("학생 연령");

주의할 점

  • switch 식의 각 갈래는 결과 타입이 공통 타입으로 맞아야 합니다.
  • 패턴은 위에서 아래로 평가되므로, 범위가 넓은 패턴을 먼저 두면 뒤의 패턴이 도달 불가능해져 컴파일 오류가 납니다.
  • _ 갈래가 없으면 처리하지 못하는 값에서 예외가 발생할 수 있으니 열거형을 제외하면 _ 를 두는 편이 안전합니다.
  • 복잡한 조건을 한 식에 모두 넣으면 오히려 읽기 어려워지므로 갈래가 길어지면 메서드로 분리합니다.

정리

switch 식은 값을 고르는 분기를 한 식으로 표현하고, 관계·논리·속성·튜플 패턴은 조건을 선언적으로 적게 해 줍니다. 단순 값 매핑부터 객체의 타입과 상태에 따른 분기까지 같은 문법으로 다룰 수 있어서 if-else 사슬을 대체하기 좋습니다.

이상으로 switch 식과 패턴 매칭에 대해 간단히 알아보았습니다.

참고