[C#] switch 완전 정리 - 값 비교를 넘어 다양한 패턴과 실무 활용
Unity 실무에서 오브젝트 타입에 따라 분기하거나, 값 범위·속성 조합으로 분기해야 할 때 switch는 가장 읽기 쉬운 선택지다.
C# 7부터 패턴 매칭이 도입되면서 switch는 단순 “값 비교”를 넘어 타입 검사, 속성 검사, 범위/논리 결합까지 지원한다.
1️⃣ 값 비교(기본)
• 상수 비교 중심
• C# 7 이전에도 가능
• 문자열, 정수, 열거형 등과 잘 맞음
var state = "Idle";
switch (state)
{
case "Idle":
// ...
break;
case "Run":
// ...
break;
default:
// ...
break;
}
2️⃣ 패턴 매칭 (C# 7+)
▪️타입 패턴 (Type Pattern)
• case Car car: 는 “target이 Car 타입이면 car 변수에 바인딩” 의미
• target이 null이어도 예외가 발생하지 않음: 어떤 패턴에도 매칭되지 않아 default로 감
public interface IOnClick { }
public class Car : MonoBehaviour, IOnClick { }
public class Tag : MonoBehaviour, IOnClick { }
public class Process : MonoBehaviour, IOnClick { }
IOnClick target = /* ... */;
switch (target)
{
case null:
// 명시적으로 null 케이스 다루기
break;
case Car car:
// car 로 안전 캐스팅 완료
break;
case Tag tag:
break;
case Process process:
break;
default:
break;
}
▪️ when 가드
• 복잡한 if/else 체인을 없애고, 조건마다 분기 행동을 깔끔하게 분리
switch (target)
{
case Car car when car.enabled:
// 조건까지 만족할 때만
break;
case Car:
// Car지만 위 조건을 만족하지 않을 때
break;
default:
break;
}
3️⃣ 값을 return하는 간결한 표현 (C# 8+)
• 매핑/포맷에 특히 유용
가장 좋아하고 애용하는 패턴
string GetCode(string key) => key switch
{
"aa" => "1",
"bb" => "2",
_ => "" // 매칭 실패 시 기본값 (필수: 식은 ‘완전성’ 요구)
};
4️⃣ 관계 패턴·논리 패턴 (C# 9+)
• <, >, >=, <= 등의 관계 패턴
• and, or, not 같은 논리 패턴 조합
• 순서가 위에서부터 평가되므로 구체적인 조건을 먼저 두는 것이 안전
string ClassifySpeed(float v) => v switch
{
< 0 => "Invalid",
0 => "Stopped",
> 0 and < 5 => "Slow",
>= 5 and < 20 => "Cruise",
>= 20 => "Fast",
_ => "Unknown"
};
5️⃣속성/튜플/위치 패턴으로 struct에 매칭(C# 8/9+)
▪️속성(Property) 패턴
string Describe(Car car) => car switch
{
{ enabled: true, tag: "Player" } => "Player Car",
{ enabled: false } => "Disabled Car",
_ => "Normal Car"
};
▪️튜플(Tuple) 패턴
(string state, int gear) key = ("Drive", 2);
var label = key switch
{
("Park", _) => "P",
("Drive", <= 2) => "D-Low",
("Drive", > 2) => "D-High",
("Neutral", _) => "N",
("Reverse", _) => "R",
_ => "?"
};
▪️위치/분해 패턴 (Deconstruct 지원 타입)
// 예시: (x, y)로 분해 가능한 타입
var text = point switch
{
(0, 0) => "Origin",
(>= 0, >= 0) => "Q1",
(<= 0, >= 0) => "Q2",
(<= 0, <= 0) => "Q3",
(>= 0, <= 0) => "Q4",
_ => "Unknown"
};
💡 실무 팁
1.분기 순서
• 구체적인 패턴을 위에, 넓은(포괄) 패턴을 아래로 적용
예: Car c when c.enabled(맨 위) → Car → default(아래)
2. 가독성 우선
• 동일 타입에서 조건만 다른 경우 when 가드로 분리하면 if-else 사슬보다 읽기 쉬움
3. switch 식 활용
• “입력 → 결과” 매핑은 3️⃣(값을 return)식이 가장 깔끔함
• Unity UI 라벨, 로그 텍스트, 상태 문자열 등에서 특히 효과적
4. 성능 관점
• 단순 값 매핑/타입 분기에서 switch vs if-else 성능 차이는 미미
• 읽기 쉽고 유지보수 쉬운 방식을 우선
5. 열거형 마지막 주의
• enum 분기 시 가능한 값 전부를 나열해 컴파일러 경고/미스 방지
• 필요 시 _ 나 default를 마지막에 배치.
예제 - 클릭 대상 타입별 처리
▪️보통 if / else 사용
void OnClicked(IOnClick target)
{
if (target is Car car)
{ /* ... */
}
else if (target is Tag tag)
{ /* ... */
}
else if (target is Process process)
{ /* ... */
}
}
▪️개선 - if / else → switch(기본적)
void OnClicked(IOnClick target)
{
switch (target)
{
case Car car when car.enabled:
// 활성 Car만
break;
case Car:
// 비활성 Car
break;
...
case null:
// NRE 방지
break;
default:
// 미래 타입 대비
break;
}
}
▪️추가 개선 - switch(기본적) → switch(Action 반환)
• 행동을 값(델리게이트) 로 만들어 반환
• 테스트/조합 용이, UI 이벤트 바인딩에도 깔끔.
Action ResolveAction(IOnClick t) => t switch
{
Car c when c.enabled => () => HandleActiveCar(c),
Car => () => HandleInactiveCar((Car)t),
Tag tg => () => HandleTag(tg),
Process pr => () => HandleProcess(pr),
null => () => { /* ignore */ },
_ => () => { /* unknown */ }
};
void OnClicked(IOnClick target) => ResolveAction(target)();
📌결론
- 단순 값 비교는 기본 switch, 매핑은 switch 식이 가장 깔끔.
- 타입/속성/범위/논리 조합이 필요하면 패턴 매칭 switch 로 가독성과 유지보수성을 올릴 것.
- null은 어떤 타입 패턴에도 매칭되지 않으므로 명시적으로 처리하거나 default에서 안전하게 마무리.
- 성능은 대부분의 실무 케이스에서 가독성 좋은 코드가 우선이며, 필요한 경우에만 프로파일링.