using System;
public class Hero { ... }
public class Warrior : Hero { ... }
public class Mage : Hero { ... }
public static class Program
{
private static void DisplayHero(Hero h)
{
if (h is Warrior) {
Console.WriteLine("Hero is a Warrior");
}
else if (h is Mage) {
Console.WriteLine("Hero is a Mage");
}
else {
Console.WriteLine("Unknown hero type! (or null?)");
}
}
public static void Run()
{
Warrior warrior = new Warrior();
Mage mage = new Mage();
DisplayHero(warrior);
DisplayHero(mage);
DisplayHero(null);
}
// output:
//
// Hero is a Warrior
// Hero is a Mage
// Unknown hero type! (or null?)
}
위의 구문을 switch 표현식을 활용하여 일치 match 결과 기반으로 실행해 보죠
using System;
public static class Program
{
private enum LightState
{
ON,
OFF,
BLINKING
}
private static string LightStateMessage(LightState state)
{
return state switch {
LightState.ON => "Tadaa!",
LightState.OFF => "It's pitch black...",
LightState.BLINKING => "Uh-oh.",
_ => "<unknown>",
};
}
public static void Run()
{
Console.WriteLine($"ON => {LightStateMessage(LightState.ON)}");
Console.WriteLine($"OFF => {LightStateMessage(LightState.OFF)}");
Console.WriteLine($"BLINKING => {LightStateMessage(LightState.BLINKING)}");
}
// output:
//
// ON => Tadaa!
// OFF => It's pitch black...
// BLINKING => Uh-oh.
}
위에 기재된 switch 은 switch 문이 아닌 표현식으로 사용합니다.
몇가지 간단한 예
null 체크 확인하기
is 키워드를 통해 nullable 여부를 체크 가능합니다.
using System;
public static class Program
{
private static bool IsInt(int? variable)
{
return (variable is int);
}
public static void Run()
{
Console.WriteLine($"11 => {IsInt(11)}");
Console.WriteLine($"null => {IsInt(null)}");
}
// output:
//
// 11 => True
// null => False
}
nullable 변수 값을 받아서 활용하는 방법도 가능합니다.
is 구문 끝에 변수 i 를 만들어서 원하는 개발 코드를 작성 합니다.
using System;
public static class Program
{
private static int? IncrementNullableInt(int? variable)
{
if (variable is int i) return i + 1;
else return null;
}
public static void Run()
{
Console.WriteLine($"11 => {IncrementNullableInt(11)}");
Console.WriteLine($"null => {IncrementNullableInt(null)}");
}
// output:
//
// 11 => 12
// null =>
}
인터페이스 Type 확인하기
is 키워드를 통해 인터페이스 타입 체크한 다음, 이를 변수를 만들어 활용 가능합니다.
using System;
using System.Collections;
using System.Collections.Generic;
public static class Program
{
private static int? Size(IEnumerable sequence)
{
if (sequence is Array array) return array.Length;
else if (sequence is IList list) return list.Count;
else if (sequence is IDictionary dict) return dict.Count;
else return null;
}
public static void Run()
{
int[] arr = new int[] { 1, 2, 3, 4, 5 };
List<int> list = new List<int>() { 1, 2 };
Dictionary<int, string> dict = new Dictionary<int, string>()
{ { 1, "a" },
{ 2, "b" },
{ 3, "c" } };
Console.WriteLine($"Size(arr) => {Size(arr)}");
Console.WriteLine($"Size(list) => {Size(list)}");
Console.WriteLine($"Size(dict) => {Size(dict)}");
Console.WriteLine($"Size(null) => {Size(null)}");
}
// output:
//
// Size(arr) => 5
// Size(list) => 2
// Size(dict) => 3
// Size(null) =>
}
열거형 또는 상수와 비교하기
열거형 enum 을 통해서 받게 되면 실수로 잘못된 입력값을 받지 않게 가이드 해주기도 합니다.
using System;
public enum ShipStatus
{
Healthy,
Damaged,
Destroyed
}
public class Ship
{
public ShipStatus status = ShipStatus.Healthy;
public bool CanFly() {
if (status == ShipStatus.Healthy) {
return true;
}
else if (status == ShipStatus.Damaged) {
return true;
}
else if (status == ShipStatus.Destroyed) {
return false;
}
else {
return false;
}
}
}
public static class Program
{
public static void Run()
{
Ship ship = new Ship();
Console.WriteLine($"STATUS: {ship.status}. Can fly? {ship.CanFly()}");
ship.status = ShipStatus.Damaged;
Console.WriteLine($"STATUS: {ship.status}. Can fly? {ship.CanFly()}");
ship.status = ShipStatus.Destroyed;
Console.WriteLine($"STATUS: {ship.status}. Can fly? {ship.CanFly()}");
}
// output:
//
// STATUS: Healthy. Can fly? True
// STATUS: Damaged. Can fly? True
// STATUS: Destroyed. Can fly? False
}
switch 명령문을 사용하여 상황을 약간 단순화 할 수도 있습니다.
public bool CanFly() {
switch (status)
{
case ShipStatus.Healthy:
return true;
case ShipStatus.Damaged:
return true;
case ShipStatus.Destroyed:
return false;
default:
return false;
}
}
하지만, switch 표현식은 더 나아가모든 중간(불필요한) 키워드와 중괄호를 완전히 제거합니다!
이는 분명히 값을 직접반환 할 때만 작동합니다. 주어진 경우에 대해 올바른 결과를 얻기 전에 계산을 수행해야 하는 경우switch 문이 더 적합할 수 있습니다.
switch 은 conditions 조건에서도 다음과 같은 표현식을사용할 수도 있습니다 .
public class Ship
{
public int healthpoints = 100;
public bool CanFly() {
return healthpoints switch {
> 50 => true,
_ => false,
};
}
}
좀 더 복잡한 데이터 사용
위의 예제는 다양한 분기에 대해 일치하는 입력이 하나뿐이었습니다.하지만 같은 유형의 두 값을 확인해야 하는 경우에는 어떻게 해야 할가요?
switch표현식 에서 여러 입력 처리
두 개로 분리된 조건에 따라 switch 구문을 사용했습니다.
public class Ship
{
public ShipStatus status = ShipStatus.Healthy;
public bool pilotIsHuman;
public bool CanFly() {
if (pilotIsHuman)
return status switch {
ShipStatus.Healthy => true,
ShipStatus.Damaged => false,
ShipStatus.Destroyed => false,
_ => false,
};
else
return status switch {
ShipStatus.Healthy => true,
ShipStatus.Damaged => true,
ShipStatus.Destroyed => false,
_ => false,
};
}
}
이를 하나로 묶어서 표현한 방법도 존재합니다.
(true, ShipStatus.Healthy) => true 의미는 "pilotIsHuman" 값이 true 이고(and) ShipStatus.Healthy 이 true 이면 결과 반환값은 true 를 의미합니다.
public class Ship
{
public ShipStatus status = ShipStatus.Healthy;
public bool pilotIsHuman;
public bool CanFly() {
return (pilotIsHuman, status) switch {
(true, ShipStatus.Healthy) => true,
(true, ShipStatus.Damaged) => false,
(true, ShipStatus.Destroyed) => false,
(false, ShipStatus.Healthy) => true,
(false, ShipStatus.Damaged) => true,
(false, ShipStatus.Destroyed) => false,
_ => false,
};
}
}
인스턴스 속성 액세스
외부에서 인스턴스의 속성에 액세스하고식이switch기본 함수에 있도록 할 수도 있습니다.
public class Ship
{
public ShipStatus status = ShipStatus.Healthy;
public bool pilotIsHuman;
}
public static class Program
{
public static void Run()
{
Ship ship = new Ship();
bool canFly = ship switch {
Ship { pilotIsHuman: true, status: ShipStatus.Healthy } => true,
Ship { pilotIsHuman: true, status: ShipStatus.Damaged } => false,
Ship { pilotIsHuman: true, status: ShipStatus.Destroyed } => false,
Ship { pilotIsHuman: false, status: ShipStatus.Healthy } => true,
Ship { pilotIsHuman: false, status: ShipStatus.Damaged } => true,
Ship { pilotIsHuman: false, status: ShipStatus.Destroyed } => false,
_ => false,
};
Console.WriteLine($"Can fly? {canFly}");
}
// output:
//
// Can fly? True
}
폐기된 값, 그리고 null 경우 (Discarded values, and the null case)
이전 예제에서는 "defalut" 케이스를 처리하고 싶을 때마다_문자를 사용했습니다.
이 기호를폐기 패턴 null이라고 하며, 표현식의 분기에서아직 일치(match) 하지 않는(null 포함) 값을 처리하도록 프로그램에 지시하는 방법입니다.
그러나 거기에 null 케이스를 포함하고 싶지 않다면 어떻게 될까요?
{} "빈 중괄호"를 사용하여, 아직 일치하지 않은 non-null value(null 이 아닌 값) 과 일치시킬 수 있습니다.
public class Ship
{
public ShipStatus status = ShipStatus.Healthy;
public float GetSpeed()
{
return this switch {
Ship { status: ShipStatus.Healthy } => 100.0f,
{} => 50.0f,
null => throw new NullReferenceException()
};
}
}
결론
Pattern matching(패턴 일치)는 코드를 더 읽기 쉽게 만드는 데 도움이 될 뿐만 아니라 모든 멋진 유형 검사와 IDE 자동 완성을 사용할 수 있도록 유지하기 때문에 C#의 정말 강력한 기능입니다!