C#

[C#] 패턴 매칭

콩스프 2023. 9. 21. 21:07

패턴 매칭

  • 식의 결과가 특정 패턴(형태)와 일치하는지 검사하는 것
  • 장황하고 거추장 스러운 분기문을 간결하게 할 수 있음

 

1) 선언 패턴

  • 주어진 식이 특졍 형식 (EX. int, string 등) 과 일치하는지를 평가
object foo = 23;
if(foo is int bar) //변수 bar를 선언
{
    Console.WriteLine(bar);
}
  • foo 식이 int와 일치하는지 판단하고 bar 변수를 선언함
  • bar는 해당 scope내에서 사용 가능

 

 

2) 형식 패턴

  • 형식 패턴은 선언 패턴과 거의 같은 방식으로 동작
  • 단, 변수 생성 없이 형식 일치 여부만 테스트
object foo = 23;

if(foo is int) //foo가 int 형식인지만 판단
{
    Console.WriteLine(foo);
}

 

 

3) 상수 패턴

  • 식이 특정 상수와 일치하는지를 검사
var GetCountryCode = (string nation) => nation switch
{
    "KR" => 82,
    "US" => 1,
    "UK" => 44,
    _ => throw new ArgumentException("Not supported Code")
};

Console.WriteLine(GetContryCode("KR"));
Console.WriteLine(GetContryCode("US"));
Console.WriteLine(GetContryCode("UK"));

 

 

4) 프로퍼티 패턴

  • 식의 속성이나 필드가 패턴과 일치하는지를 검사
class Car
{
    public string Model { get; set; }
    public DateTime ProducedAt{ get; set; }
}

static string GetNickname(Car car)
{
    var GenerateMessage = (Car car, string nickname) =>
        $"{car.Model} produced in {car.ProduceAt.Year} is {nickname}";
        
        if(car is Car { Model:"Mustang", ProducedAt.Year:1967})
            return GenerateMessage(car, "Fastback");
        else if (car is Car { Model:"Mustang", ProducedAt.Year:1976})
            return GenerateMessage(car, "Cobra II");
        else
            return GenerateMessage(car, "Unknown");
}
  • car 객체가 해당 프로퍼티 패턴과 일치하는지 검사

 

 

5) 관계 패턴

  • 관계 연산자를 이용하여 입력받은 식을 상수와 비교
  • 관계 연산자 : >, >=, ==, !=, <, <=
static string GetGrade(double score) => score switch
{
    < 60 => "F",
    >= 60 and < 70 => "D",
    >= 70 and < 80 => "C",
    >= 80 and < 90 => "B",
    _ => "A",
};

 

 

6) 논리 패턴

  • 복수의 패턴을 패턴 논리 연산자(and, or, not)로 조합
class OrderItem
{
    public int Amount{ get; set; }
    public int Price { get; set; }
}

static double GetPrice(OrderItem orderItem) => orderItem switch
{
    OrderItem { Amount: 0 } or OrderItem { Price: 0 }
        => 0.0,
    OrderItem { Amount: >= 0 } or OrderItem { Price: >= 10_000 }
    	=> orderItem.Amount * orderItem.Price * 0.8,
    not OrderItem { Amount: < 100 }
    	=> orderItem.Amount * orderItem.Price * 0.9,
    _ => orderItem.Amount * orderItem.Price,
};

 

 

7) 괄호 패턴

  • 괄호 ()로 패턴을 감쌈
object age = 30;

if(age is (int and >19))
    Console.WriteLine("Major");

 

 

8) 위치 패턴

  • 식의 결과를 분해(Deconstruct)하고, 분해된 값들이 내장된 복수의 패턴과 일치하는지를 검사
  • 내장되는 패턴에는 형식 패턴, 상수 패턴 등 모든 패턴 사용 가능
Tuple<string, int> itemPrice = new Tuple<string, int>("espresso", 3000);

if( itemPrice is ("espresso", < 5000))
{
    Console.WriteLine("The coffee is affordable.");
}

 

 

9) var 패턴

  • null을 포함한 모든 식의 패턴 매칭을 성공시키고, 그 식의 결과를 변수에 할당
  • 식의 결과를 임시 변수에 할당한 다음 추가적인 연산을 수행하고자 할 때 유용
//평균이 60점 이상인 경우에만 Pass
var IsPassed =
    (int[] scores) => scores.Sum() / scores.Length is var average
    && average >= 60;
    
    int[] scores1 = { 90, 80, 60, 80, 70 };
    Console.WriteLine($"{string.Join(",", scores1)}: Pass:{IsPassed(scores1)}" //true
    
    int[] scores2 = { 90, 80, 59, 80, 70};
    Console.WriteLine($"{string.Join(",", scores2)}: Pass:{IsPassed(scores2)}" //false

 

 

10) 무시 패턴

  • var 패턴처럼 모든 식과의 패턴 일치 검사를 성공
  • 단, is 식에서는 사용할 수 없고, switch 식에서만 사용 가능
var GetCountryCode = (string nation) => nation switch
{
    "KR" => 82,
    "US" => 1,
    "UK" => 44,
    _ => throw new ArgumentException("Not supported Code")
};

Console.WriteLine(GetContryCode("KR"));
Console.WriteLine(GetContryCode("US"));
Console.WriteLine(GetContryCode("UK"));

 

 

11) 목록 패턴

  • 배열이나 리스트(List)가 패턴의 시퀀스와 일치하는지를 검사
  • 패턴의 시퀀스는 ' [ ' 와 ' ] ' 사이에 패턴의 목록을 입력하여 생성
var match = (int[] array) => array is [int, >10, _];

Console.WriteLine(match(new int[] { 1, 100, 3 })); //True
Console.WriteLine(match(new int[] { 100, 10, 999 } //False

var match = (int[] array) => array is [int, >10, ..];

Console.WriteLine(match(new int[] { 1, 100, 101, 102, 103, 104 })); //True
Console.WriteLine(match(new int[] { 100, 10, 999 } //False