C# 프로그래밍 기초 실습 전자책
C# 7.0 패턴 (Pattern)

패턴매칭이란 추상적 의미에서 어떤 대상이 특정한 '모양'(패턴)을 가지고 있는가를 테스트하는 것으로, 예를 들어, 어떠한 객체 리스트에서 특정한 성격을 갖는 객체들을 추출해 낼 때 패턴 매칭을 사용할 수 있다.

C# 7 에서는 const pattern, type pattern, var pattern 이라는 3가지 패턴을 지원하고 있는데, 이후 C# 버전에서 더 많은 패턴들을 추가할 예정이다 (주: C# 7 개발 중에 논의되었던 일부 고급 패턴 매칭은 제외됨). C# 7 에서 이러한 패턴들이 일치하는지를 체크하는 패턴 매칭 (pattern matching) 기능은 is 연산자switch 문에서 지원하고 있다.




패턴매칭: is 연산자

이전 C# 버전에서 is 연산자는 어떤 객체가 지정된 타입의 객체인지를 체크하는 용도로 사용되었다. 예를 들어, "A is string" 은 A가 문자열(string) 타입인지 체크하여 맞으면 true를 리턴한다. 여기서 is 연산자 뒤에는 항상 Type명을 지정하여야 했다.

C# 7 에서 is 연산자는 is 뒤에 Type명 이외에 패턴(Pattern)을 사용할 수 있게 되었다. 예를 들어, 아래 예제와 같이 data 컬렉션이 있을 때, 이 중 특정 패턴의 요소를 찾아내고 싶다고 가정해 보자. 패턴을 찾기 위해 상수값을 사용할 수도 있을 것이고, 특정 타입인지를 체크할 수도 있을 것이다.

예제

object[] data = { 1, null, 10, new Circle(5), new Person("Lee"), "" };

foreach (var item in data)
{
    // 패턴 찾기
}


상수 패턴(const pattern)은 특정 상수값을 사용해서 패턴을 체크하는 것이다. 예를 들어, 아래 예제의 if 조건문에서 item 이 null 인지 아니면 상수값 10 인지를 체크하고 있다. 여기서 is 연산자를 사용하고 있는데, is 뒤에 상수값을 직접 대입하여 사용하였다. 이러한 표현은 C# 7 이전에는 허용되지 않았던 표현이고, 이전 버젼에서는 대신 동등연산자 == 를 사용하였다.

예제

object[] data = { 1, null, 10, new Circle(5), new Person("Lee"), "" };

foreach (var item in data)
{
    if (item is null)   // const pattern
    {
        Console.WriteLine("NULL 찾았음");
    }
    else if (item is 10)  // const pattern
    {
        Console.WriteLine("10 찾았음");
    }
}


타입 패턴(type pattern)은 어떤 객체가 해당 타입의 혹은 그 파생클래스의 객체인지, 또는 어떤 인터페이스 타입을 가지고 있는지 등을 체크하는 것으로 사실 이는 기존의 is 연산자가 제공했던 기능이다. 다만, 다른 차이점은 C# 7에서는 해당 타입에 상응하는 새로운 변수를 제공한다는 점이다.
아래 예제에서 변수 item은 object 타입인데, 이것이 Circle 객체인지 체크하면서 Circle 타입인 경우 item 객체를 Circle 타입의 객체로 변환하여 새로운 변수 circ 에 넣게 된다. 이후, 이 변수 circ는 자신을 둘러싼 블럭 (enclosing block) 범위 내에서 사용될 수 있는데, 아래 예에서 보듯이 circ.Radius 와 같이 Circle 타입의 속성을 캐스팅 없이 직접 사용할 수 있다.

예제

object[] data = { 1, null, 10, new Circle(5), new Person("Lee"), "" };

foreach (object item in data)
{
    if (item is Circle circ)  // type pattern
    {
        WriteLine(circ.Radius);
    }
}


var 패턴(var pattern)은 타입패턴에서 타입 대신 var 를 쓰는 것으로, 모든 타입은 var 변수에 할당할 수 있으므로 is 연산자에서 이는 항상 참이 된다. var 패턴은 아래 switch 문에서 좀 더 자세히 설명한다.
패턴매칭: switch 문

이전 C# 버전에서 switch 문은 int, string, bool 등의 기본적인 데이타 타입에서만 사용할 수 있었다. 하지만, C# 7 부터는 switch 문이 패턴 매칭을 위해 사용될 수 있게 되었다. 즉, case 문에 위에서 언급한 3가지 패턴들이 사용될 수 있는 것이다.

아래 예제에서 switch 문은 Shape 라는 클래스 객체를 받아들이고 있다. C# 7 이전에서는 이러한 클래스 객체를 switch에서 받아들일 수 없었다. switch 문의 case 에서는 상수패턴, 타입패턴, var 패턴을 사용할 수 있다. 아래 예제에서는 첫번째 case 에서 상수패턴으로 null 을 체크하였고, 두번째 ~ 네번째에서 타입패턴을 체크하고 있다. 두번째 타입패턴은 "Circle c" 로서 만약 해당 Shape 이 Circle 객체이면 변수 c 에 Circle 로 캐스팅된 객체를 넣게 되고 이를 case 블럭에서 사용할 수 있게 된다. case 블럭에서 정의된 새로운 변수는 case 블럭 안에서만 사용할 수 있다.

예제

private void PrintArea(List<Shape> shapes)
{
    foreach (var shape in shapes)
    {
        switch (shape)
        {
            // const pattern
            case null:
                WriteLine("Skip");
                break;

            // type pattern
            case Circle c:
                WriteLine($"원: {c.Radius * c.Radius * Math.PI}");
                break;
            case Rect r when r.Width == r.Height:
                WriteLine($"정사각형: {r.Width * r.Width}");
                break;
            case Rect r:
                WriteLine($"사각형: {r2.Width * r2.Height}");
                break;

            default:
                WriteLine("모르는 모양");
                break;
        }
    }
}

class Shape {}
class Circle : Shape { /* 생략 */ }
class Rect : Shape { /* 생략 */ }


세번째 "Rect r"은 뒤에 when 문을 사용하였는데, 여기서 r.Width 와 r.Height가 같은지 즉 정사각형인지 체크하게 된다. 네번째 "Rect r"는 세번째 정사각형 체크에서 실패한 일반 사각형을 위한 case 문이다. switch 문 내의 case 문은 순서대로 실행되므로 세번째와 네번째 case 문이 바뀌면 정사각형 체크는 물론 실행되지 않을 것이다.

다음으로 var 패턴에 대해 한번 살펴보자. 언뜻 var 패턴은 크게 쓸 용도가 없는 것처럼 보이지만, case 문에서 when 과 연동하여 유용하게 사용될 수도 있다. 아래 예제는 고객의 ID를 체크하여 어떤 종류의 고객인지 판별하는 간단한 예인데, 여기서 var 패턴을 활용하여 when 문에서 ID가 속한 그룹을 찾아내고 있다.

예제

List<int> vip = new List<int> { 1, 3, 5, 9 };
List<int> active = new List<int> { 10, 13, 15, 19 };
List<int> blacklist = new List<int> { 7, 14, 12, 133 };

private void CheckCustomer(int id)
{
    switch(id)
    {
        case var _id when (vip.Contains(_id)):
            WriteLine("VIP");
            // VIP 
            break;
        case var _id when (active.Contains(_id)):
            //...
            break;
        case var _id when (blacklist.Contains(_id)):
            //...
            break;
        default:
            //...
            break;
    }
}


본 웹사이트는 광고를 포함하고 있습니다. 광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.





Comment 의견/코멘트 쓰기
이메일
 *정확한 이메일이 아닐 경우 삭제될 수 있습니다
의견/코멘트
좌측의 3자리 문자들을 입력해 주십시오





아티클 모바일 링크
C# 스터디 소셜미디어
쉽게 배우는 파이썬 기초