CodeGym /행동 /C# SELF /타입 다루기: is,

타입 다루기: is, as 그리고 Pattern Matching

C# SELF
레벨 34 , 레슨 4
사용 가능

1. is 연산자: 타입 호환성 체크

실제 앱을 만들다 보면, 기본 클래스 타입의 객체 참조를 가지고 있지만 실제 메모리에는 더 구체적인 파생 타입이 들어있는 경우가 많아. 이럴 때 파생 타입만의 멤버에 접근해야 할 수도 있지. 이런 상황에서 C#은 is, as 연산자와, 특히 최신 버전에서는 pattern matching이라는 강력한 기능을 제공해.

is는 객체가 특정 타입, 그 파생 타입, 또는 지정한 인터페이스의 인스턴스인지 확인할 수 있어. 만약 객체가 그 타입과 호환되면 true를, 아니면 false를 반환해.

주요 용도: 객체를 특정 타입으로 안전하게 변환할 수 있는지 확인할 때.

예시: 기본 타입과 상속 체크


class Animal { public string Species { get; set; } = "알수없음"; }
class Dog : Animal { public void Bark() => Console.WriteLine("멍멍!"); }
class Cat : Animal { public void Meow() => Console.WriteLine("야옹!"); }

Animal myAnimal = new Dog { Species = "골든 리트리버" };

Console.WriteLine($"myAnimal is Animal: {myAnimal is Animal}"); // True
Console.WriteLine($"myAnimal is Dog: {myAnimal is Dog}");       // True
Console.WriteLine($"myAnimal is Cat: {myAnimal is Cat}");       // False

이 예시에서 myAnimal은 실제로 Dog야. 그래서 myAnimal is AnimalmyAnimal is Dogtrue를 반환해.

예시: null 체크

is 연산자는 null과도 예측 가능하게 동작해.


Animal nullAnimal = null;
Dog specificDog = new Dog();

Console.WriteLine($"nullAnimal is Animal: {nullAnimal is Animal}"); // False (null은 어떤 타입의 인스턴스도 아님)
Console.WriteLine($"specificDog is null: {specificDog is null}");   // False (객체는 null 아님)

null은 어떤 타입의 인스턴스도 아니기 때문에 null is MyType은 항상 false야. 하지만 someObject is nullnull인지 안전하게 확인하는 좋은 방법이야.

예시: is와 인터페이스

is 연산자는 인터페이스 구현 여부도 체크할 수 있어.


interface IFlyable { void Fly(); }
class Bird : Animal, IFlyable { public void Fly() => Console.WriteLine("파닥파닥!"); }
class Fish : Animal { }

Animal creature = new Bird();

Console.WriteLine($"creature is Bird: {creature is Bird}");     // True
Console.WriteLine($"creature is IFlyable: {creature is IFlyable}"); // True

creature = new Fish();
Console.WriteLine($"creature is IFlyable: {creature is IFlyable}"); // False

2. as 연산자: 안전한 타입 변환

as 연산자는 객체를 지정한 타입으로 안전하게 변환할 때 써. (Type)obj처럼 명시적 캐스팅은 실패하면 InvalidCastException을 던지지만, as는 변환이 불가능하면 null을 반환해. 그래서 실제 타입을 확신할 수 없을 때 딱 좋아.

주요 용도: 타입 변환을 시도하고, 실패하면 null을 받기.

예시: as 기본 사용


class Shape { }
class Circle : Shape { public double Radius { get; set; } }
class Square : Shape { public double Side { get; set; } }

Shape myShape = new Circle { Radius = 5.0 };

// Circle로 변환 시도
Circle circle = myShape as Circle;
if (circle != null) // 꼭 null 체크!
{
    Console.WriteLine($"이건 반지름이 {circle.Radius}인 원이야"); // 출력: 이건 반지름이 5인 원이야
}

// Square로 변환 시도
Square square = myShape as Square;
if (square == null) // 변환 실패, square == null
{
    Console.WriteLine("이건 정사각형이 아니야."); // 출력: 이건 정사각형이 아니야.
}

보다시피 as는 런타임 에러 없이 null을 반환해서 안전하게 쓸 수 있어.

예시: as의 한계

중요한 점! as 연산자는 참조 타입과 nullable 값 타입에만 쓸 수 있어. 일반 값 타입에는 적용 안 돼. 값 타입은 null을 가질 수 없으니까.


object someValue = 100;

int num = someValue as int; // 컴파일 에러: 'as'는 null이 될 수 없는 타입에 쓸 수 없음
int? nullableNum = someValue as int?; // OK, nullable int
Console.WriteLine($"Nullable int: {nullableNum}"); // 출력: Nullable int: 100

string str = someValue as string; // 100은 문자열이 아니므로 null 반환
Console.WriteLine($"String from int: {str ?? "null"}"); // 출력: String from int: null

null이 될 수 없는 값 타입에는 명시적 캐스팅((int)someValue)을 쓰거나, 더 나은 방법은 pattern matching을 쓰는 거야.

3. Pattern Matching (패턴 매칭)

pattern matching은 타입 체크와 데이터 추출을 더 우아하고 안전하게 할 수 있는 강력한 기능이야. 반복되는 코드를 줄이고 가독성도 좋아져.

Type Pattern (is와 타입 패턴)

가장 자주 쓰는 패턴 매칭 형태로, 객체의 타입을 확인하고, 성공하면 바로 그 타입의 새 변수에 할당할 수 있어.

문법: 표현식 is 타입 변수

예시: is + 캐스팅 대체


// Shape, Circle, Square 클래스 계속 사용

Shape currentShape = new Circle { Radius = 7.5 };

// 옛날 방식:
if (currentShape is Circle)
{
    Circle c = (Circle)currentShape;
    Console.WriteLine($"옛날 방식: 반지름 {c.Radius}인 원");
}

// 새로운, 간단한 타입 패턴 방식
if (currentShape is Circle c) // 타입 체크 + 'c' 변수 생성
{
    Console.WriteLine($"새로운 방식: 반지름 {c.Radius}인 원"); // 'c'는 이미 Circle 타입
}

Shape anotherShape = new Square { Side = 10.0 };
if (anotherShape is Square s)
{
    Console.WriteLine($"이건 한 변이 {s.Side}인 정사각형이야");
}

c (또는 s) 변수는 if 블록 안에서만 쓸 수 있어서, 잘못된 타입에서 실수로 쓰는 걸 막아줘.

Property Pattern (프로퍼티 패턴)

C# 8.0부터는 타입 체크뿐 아니라, 객체의 한두 개 프로퍼티 값을 동시에 확인하고 새 변수로 추출할 수도 있어.

문법: 표현식 is 타입 { 프로퍼티1: 값_패턴, 프로퍼티2: 추출_변수 }

예시: 프로퍼티 체크


Shape testShape = new Circle { Color = "초록", Radius = 12.0 };

// 객체가 원이고 색이 초록인지 확인
if (testShape is  Circle { Color: "초록" })
{
    Console.WriteLine("초록색 원을 찾았어.");
}

// 객체가 원이고, 반지름을 추출
if (testShape is  Circle { Radius: var r })
{
    Console.WriteLine($"반지름 추출됨: {r}");
}

// 체크와 추출을 동시에:
if (testShape is  Circle { Color: "초록", Radius: var radiusVal } circleObj)
{
    Console.WriteLine($"초록색 원 추출됨. 반지름: {radiusVal}, 객체: {circleObj.Radius}");
}

프로퍼티 패턴은 복잡한 조건문을 엄청 간단하게 만들어줘.

예시: 프로퍼티 패턴에서 범위와 논리 연산자

이제 객체의 프로퍼티가 특정 조건을 만족하는지도 체크할 수 있어:


Circle bigCircle = new Circle { Radius = 25.0, Color = "파랑" };

// 반지름이 20보다 크고 색이 파랑인지 확인
if (bigCircle is Circle { Radius: > 20, Color: "파랑" })
{
    Console.WriteLine("큰 파란 원 발견!");
}

// 반지름이 (5..15) 범위인지 확인
if (testShape is Circle { Radius: >= 5 and <= 15 })
{
    Console.WriteLine("중간 크기 원이야.");
}

중요! bool 타입이 아니라, 여기서는 and, or, not 키워드를 써.

4. Switch Expressions와 Switch Statements에서 Pattern Matching

pattern matching의 진가는 switch에서 발휘돼. 패턴에 따라 다양한 로직을 실행할 수 있거든.

예시: Switch Statement

각 패턴에 맞는 코드 블록을 지정할 수 있어.


// Animal, Dog, Cat 클래스는 강의 처음과 동일
Animal currentCreature = new Dog { Species = "푸들" };

switch (currentCreature)
{
    case Dog d: // 타입 패턴: Dog면 'd' 변수에 할당
        d.Bark();
        Console.WriteLine($"이건 {d.Species} 종의 개야.");
        break;
    case Cat c when c.Species == "샴": // 타입 패턴 + 조건 (when)
        c.Meow();
        Console.WriteLine($"이건 샴 고양이야.");
        break;
    case Animal a: // 그냥 Animal (Dog/Cat 아님)
        Console.WriteLine($"이건 그냥 {a.Species} 종류의 동물이야.");
        break;
    case null: // null 패턴
        Console.WriteLine("객체가 null이야.");
        break;
    default: // 위에 해당 안 되면
        Console.WriteLine("알 수 없는 생명체야.");
        break;
}

switch에서 case 순서가 중요해! 더 구체적인 패턴이 먼저 와야 해.

예시: Switch Expression

switch의 더 간단한 문법으로, 값을 반환해. 타입이나 프로퍼티에 따라 객체를 다른 값으로 바꿀 때 딱이야.

문법: 표현식 switch { 패턴1 => 결과1, 패턴2 => 결과2, ... _ => 기본_결과 }


Shape processShape = new Rectangle { Width = 5, Height = 5, Color = "빨강" };

string shapeInfo = processShape switch
{
    Circle { Radius: var r } when r > 10 => $"큰 원 (R={r})", // 프로퍼티 패턴 + 조건
    Circle { Color: "파랑" } c =>  $"파란 원 (R={c.Radius})",   // 프로퍼티 패턴 + 추출
    Circle c =>  $"일반 원 (R={c.Radius})",                     // 타입 패턴
    Rectangle { Width: var w, Height: var h } when w == h =>  $"정사각형 ({w}x{h})", // 정사각형
    Rectangle r =>  $"직사각형 ({r.Width}x{r.Height})",         // 그 외 직사각형
    null =>  "도형이 없음 (null)",                           // null 패턴
    _ =>  "알 수 없는 도형"                                     // 디스카드 패턴 (_)
};

Console.WriteLine(shapeInfo); // 출력: 정사각형 (5x5)

switch expression은 짧고 직관적인 변환에 진짜 좋아.

Var Pattern (var 패턴)

C# 7.0부터 pattern matching에서 var를 쓸 수 있어. var는 (null 제외) 어떤 객체와도 매칭되고, 그 값을 해당 타입의 변수로 추출해.

예시: var 패턴 사용


object obj = "Hello World";

if (obj is var  result) // 항상 true, result는 string
{
    Console.WriteLine($"타입: {result.GetType().Name}, 값: {result}");
}

obj = 123;
string typeName = obj switch
{
    var x when x is int => "이건 정수야",
    var y when y is string =>  "이건 문자열이야",
    _ => "다른 거"
};
Console.WriteLine(typeName); // 출력: 이건 정수야

var 패턴은 타입 체크보다는, 다른 패턴이나 switch 안에서 값 추출할 때 유용해.

5. is vs as vs Pattern Matching: 언제 뭘 써야 할까?

어떤 도구를 쓸지는 네가 뭘 하고 싶은지, 그리고 C# 버전에 따라 달라져.

is 연산자 (단순):

언제 쓰나: 객체가 특정 타입인지 그냥 확인만 하고, 바로 캐스팅이나 멤버 접근이 필요 없을 때.


    if (myObject is SomeType)

as 연산자:

언제 쓰나: 참조 타입(또는 nullable 값 타입)을 변환 시도하고, 실패 시 null 체크로 예외 없이 처리하고 싶을 때.


    MyDerivedClass derived = myBaseObject as MyDerivedClass;
if (derived != null) { /* ... */ }

Pattern Matching (is Type variable):

최신 추천 방식: 타입 체크와 안전한 변환을 한 줄로, 더 읽기 쉽게 할 수 있어. 모든 타입에 사용 가능.

언제 쓰나: 타입 체크 후 바로 그 타입의 멤버를 쓰고 싶을 때.


    if (myObject is MyDerivedClass derived) { derived.SpecificMethod(); }

Pattern Matching (switch expressions / statements):

최신 추천 방식: 객체 타입, 프로퍼티 등 다양한 조건에 따라 여러 동작이나 값을 반환해야 할 때. if-else if 체인보다 훨씬 깔끔해.

언제 쓰나: 객체 특성에 따라 다양한 동작(폴리모픽 행동)이나 복잡한 선택 로직이 필요할 때.


string GetShapeInfo(Shape s) => s switch
{
    Circle c => $"원 R={c.Radius}",
    Rectangle r => $"직사각형 W={r.Width} H={r.Height}",
    _ => "알 수 없음"
};
1
설문조사/퀴즈
지연 실행, 레벨 34, 레슨 4
사용 불가능
지연 실행
컬렉션 작업 최적화
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION