콘텐츠로 이동

C# Pattern Matching 심층 가이드

패턴 매칭(Pattern Matching)은 값의 형태(shape) 를 검사해 분기하는 기법입니다. C# 7부터 단계적으로 확장되어 C# 11에서 리스트 패턴까지 지원합니다. 복잡한 if-else 체인과 캐스트 코드를 간결하고 안전하게 표현합니다.


// C# 7: is 타입 패턴
object obj = "hello";
if (obj is string s)
{
Console.WriteLine(s.ToUpper()); // 타입 검사 + 선언 동시에
}
// 부정: is not
if (obj is not int)
{
Console.WriteLine("정수가 아님");
}

switch 문을 표현식으로 사용해 값을 반환합니다.

// 전통적 switch 문
string GetDayType(DayOfWeek day)
{
switch (day)
{
case DayOfWeek.Saturday:
case DayOfWeek.Sunday:
return "주말";
default:
return "평일";
}
}
// switch 표현식 (C# 8+)
string GetDayType(DayOfWeek day) => day switch
{
DayOfWeek.Saturday or DayOfWeek.Sunday => "주말",
_ => "평일"
};
abstract class Shape { }
class Circle : Shape { public double Radius; }
class Rectangle : Shape { public double W, H; }
class Triangle : Shape { public double Base, Height; }
double GetArea(Shape shape) => shape switch
{
Circle c => Math.PI * c.Radius * c.Radius,
Rectangle r => r.W * r.H,
Triangle t => 0.5 * t.Base * t.Height,
null => throw new ArgumentNullException(nameof(shape)),
_ => throw new NotSupportedException($"Unknown shape: {shape.GetType()}")
};

객체의 속성값을 패턴으로 매칭합니다.

record Point(int X, int Y);
string ClassifyPoint(Point p) => p switch
{
{ X: 0, Y: 0 } => "원점",
{ X: 0 } => "Y축 위",
{ Y: 0 } => "X축 위",
{ X: > 0, Y: > 0 } => "1사분면",
{ X: < 0, Y: > 0 } => "2사분면",
{ X: < 0, Y: < 0 } => "3사분면",
_ => "4사분면"
};
Console.WriteLine(ClassifyPoint(new Point(3, 4))); // 1사분면
record Address(string City, string Country);
record Person(string Name, Address Address);
string GetRegion(Person person) => person switch
{
{ Address: { Country: "KR", City: "Seoul" } } => "서울",
{ Address: { Country: "KR" } } => "한국 (서울 외)",
{ Address: { Country: "US" } } => "미국",
_ => "기타"
};

Deconstruct 메서드가 있는 타입을 분해해 매칭합니다.

record Point(int X, int Y)
{
public void Deconstruct(out int x, out int y) => (x, y) = (X, Y);
}
string Quadrant(Point p) => p switch
{
(0, 0) => "원점",
(> 0, > 0) => "1사분면",
(< 0, > 0) => "2사분면",
(< 0, < 0) => "3사분면",
(> 0, < 0) => "4사분면",
(0, _) => "Y축",
(_, 0) => "X축",
_ => "알 수 없음"
};

비교 연산자를 패턴으로 사용합니다.

string GetGrade(int score) => score switch
{
>= 90 => "A",
>= 80 => "B",
>= 70 => "C",
>= 60 => "D",
_ => "F"
};
// 논리 패턴: and, or, not
bool IsValidTemperature(double temp) => temp is >= -273.15 and <= 1000.0;
bool IsWeekend(DayOfWeek day) => day is DayOfWeek.Saturday or DayOfWeek.Sunday;

배열이나 리스트의 구조를 패턴으로 매칭합니다.

int[] numbers = { 1, 2, 3, 4, 5 };
bool result = numbers switch
{
[] => true, // 빈 배열
[1] => true, // 원소 1개, 값이 1
[1, 2, ..] => true, // 1로 시작하고 2가 두 번째
[.., 5] => true, // 마지막이 5
[1, .., 5] => true, // 1로 시작, 5로 끝
_ => false
};
// 슬라이스 패턴으로 요소 캡처
string Describe(int[] arr) => arr switch
{
[] => "빈 배열",
[var x] => $"원소 1개: {x}",
[var h, .. var rest] => $"첫 번째: {h}, 나머지 {rest.Length}"
};

패턴에 추가 조건을 붙입니다.

record Order(string Status, decimal Amount);
string ProcessOrder(Order order) => order switch
{
{ Status: "pending" } when order.Amount > 100_000 => "고액 결제 검토 필요",
{ Status: "pending" } => "결제 대기",
{ Status: "paid" } => "결제 완료",
{ Status: "cancelled" } => "취소됨",
_ => $"알 수 없는 상태: {order.Status}"
};

항상 매칭되며 값을 캡처합니다.

// var 패턴은 항상 true, 값을 변수에 바인딩
if (GetValue() is var result && result != null)
{
Console.WriteLine(result);
}
// switch에서 공통 처리
string Handle(object obj) => obj switch
{
int n when n < 0 => $"음수: {n}",
int n => $"양수: {n}",
var x => $"기타: {x?.GetType().Name}"
};

abstract record JsonValue;
record JsonNull : JsonValue;
record JsonBool(bool Value) : JsonValue;
record JsonNumber(double Value) : JsonValue;
record JsonString(string Value) : JsonValue;
record JsonArray(JsonValue[] Items) : JsonValue;
string Stringify(JsonValue value) => value switch
{
JsonNull => "null",
JsonBool { Value: true } => "true",
JsonBool { Value: false } => "false",
JsonNumber { Value: var n } => n.ToString("G"),
JsonString { Value: var s } => $"\"{s}\"",
JsonArray { Items: [] } => "[]",
JsonArray { Items: var items } =>
$"[{string.Join(", ", items.Select(Stringify))}]",
_ => throw new NotSupportedException()
};

패턴C# 버전예시
타입 패턴7.0obj is string s
switch 표현식8.0x switch { ... }
속성 패턴8.0{ X: 0, Y: 0 }
위치 패턴8.0(0, 0)
관계 패턴9.0>= 90
논리 패턴9.0and, or, not
리스트 패턴11.0[1, .., 5]

패턴 매칭은 가독성과 안전성을 동시에 높입니다. 특히 도메인 모델의 계층 구조를 처리할 때 상속 기반 다형성의 좋은 대안이 됩니다.