다음을 통해 공유


패턴 일치 및 is 및 as 연산자를 사용하여 안전하게 캐스팅하는 방법

개체는 다형적이므로 기본 클래스 형식의 변수가 파생 형식을 보유할 수 있습니다. 파생 형식의 인스턴스 멤버에 액세스하려면 값을 파생 형식으로 다시 캐스팅 해야 합니다. 그러나 캐스트는 InvalidCastException를 던질 위험을 초래합니다. C#은 성공할 경우에만 조건부로 캐스트를 수행하는 패턴 매칭 문을 제공합니다. 또한 C#은 값이 특정 형식인지 테스트할 is 및 as 연산자를 제공합니다.

다음 예제에서는 패턴 일치 is 문을 사용하는 방법을 보여 줍니다.

var g = new Giraffe();
var a = new Animal();
FeedMammals(g);
FeedMammals(a);
// Output:
// Eating.
// Animal is not a Mammal

SuperNova sn = new SuperNova();
TestForMammals(g);
TestForMammals(sn);
// Output:
// I am an animal.
// SuperNova is not a Mammal

static void FeedMammals(Animal a)
{
    if (a is Mammal m)
    {
        m.Eat();
    }
    else
    {
        // variable 'm' is not in scope here, and can't be used.
        Console.WriteLine($"{a.GetType().Name} is not a Mammal");
    }
}

static void TestForMammals(object o)
{
    // You also can use the as operator and test for null
    // before referencing the variable.
    var m = o as Mammal;
    if (m != null)
    {
        Console.WriteLine(m.ToString());
    }
    else
    {
        Console.WriteLine($"{o.GetType().Name} is not a Mammal");
    }
}

class Animal
{
    public void Eat() { Console.WriteLine("Eating."); }
    public override string ToString()
    {
        return "I am an animal.";
    }
}
class Mammal : Animal { }
class Giraffe : Mammal { }

class SuperNova { }

앞의 샘플에서는 패턴 일치 구문의 몇 가지 기능을 보여 줍니다. 이 문은 if (a is Mammal m) 테스트를 초기화 할당과 결합합니다. 할당은 테스트가 성공하는 경우에만 발생합니다. 변수 mif 문에서 할당될 때만 그 범위 내에서 사용 가능합니다. 동일한 메서드에서 나중에 m을(를) 액세스할 수 없습니다. 앞의 예제에서는 연산자를 사용하여 개체를 as 지정된 형식으로 변환하는 방법도 보여줍니다.

다음 예제와 같이 nullable 값 형식에 값 이 있는지 테스트하는 데 동일한 구문을 사용할 수도 있습니다.

int i = 5;
PatternMatchingNullable(i);

int? j = null;
PatternMatchingNullable(j);

double d = 9.78654;
PatternMatchingNullable(d);

PatternMatchingSwitch(i);
PatternMatchingSwitch(j);
PatternMatchingSwitch(d);

static void PatternMatchingNullable(ValueType? val)
{
    if (val is int j) // Nullable types are not allowed in patterns
    {
        Console.WriteLine(j);
    }
    else if (val is null) // If val is a nullable type with no value, this expression is true
    {
        Console.WriteLine("val is a nullable type with the null value");
    }
    else
    {
        Console.WriteLine("Could not convert " + val.ToString());
    }
}

static void PatternMatchingSwitch(ValueType? val)
{
    switch (val)
    {
        case int number:
            Console.WriteLine(number);
            break;
        case long number:
            Console.WriteLine(number);
            break;
        case decimal number:
            Console.WriteLine(number);
            break;
        case float number:
            Console.WriteLine(number);
            break;
        case double number:
            Console.WriteLine(number);
            break;
        case null:
            Console.WriteLine("val is a nullable type with the null value");
            break;
        default:
            Console.WriteLine("Could not convert " + val.ToString());
            break;
    }
}

앞의 샘플에서는 변환에 사용할 패턴 일치의 다른 기능을 보여 줍니다. null 패턴의 변수를 테스트하기 위해 null 값을 구체적으로 확인할 수 있습니다. 변수의 런타임 값이 null일 때, is 형식을 확인하는 문은 항상 false를 반환합니다. 패턴 일치 is 문은 nullable 값 형식(예: int? 또는 Nullable<int>)을 허용하지 않지만 다른 값 형식을 테스트할 수 있습니다. 앞의 예제의 패턴은 is nullable 값 형식으로 제한되지 않습니다. 이러한 패턴을 사용하여 참조 형식의 변수에 값이 있는지 또는 null인지 테스트할 수도 있습니다.

앞의 샘플에서는 변수가 다양한 형식 중 하나일 수 있는 문에서 switch 형식 패턴을 사용하는 방법도 보여 줍니다.

변수가 지정된 형식인지 테스트하고 새 변수에 할당하지 않으려면 참조 형식과 nullable 값 형식에 대해 isas 연산자를 사용할 수 있습니다. 다음 코드에서는 패턴 일치가 도입되기 전에 C# 언어의 일부였던 문 및 is 문을 사용하여 as 변수가 지정된 형식인지 테스트하는 방법을 보여 줍니다.

// Use the is operator to verify the type.
// before performing a cast.
Giraffe g = new();
UseIsOperator(g);

// Use the as operator and test for null
// before referencing the variable.
UseAsOperator(g);

// Use pattern matching to test for null
// before referencing the variable
UsePatternMatchingIs(g);

// Use the as operator to test
// an incompatible type.
SuperNova sn = new();
UseAsOperator(sn);

// Use the as operator with a value type.
// Note the implicit conversion to int? in
// the method body.
int i = 5;
UseAsWithNullable(i);

double d = 9.78654;
UseAsWithNullable(d);

static void UseIsOperator(Animal a)
{
    if (a is Mammal)
    {
        Mammal m = (Mammal)a;
        m.Eat();
    }
}

static void UsePatternMatchingIs(Animal a)
{
    if (a is Mammal m)
    {
        m.Eat();
    }
}

static void UseAsOperator(object o)
{
    Mammal? m = o as Mammal;
    if (m is not null)
    {
        Console.WriteLine(m.ToString());
    }
    else
    {
        Console.WriteLine($"{o.GetType().Name} is not a Mammal");
    }
}

static void UseAsWithNullable(System.ValueType val)
{
    int? j = val as int?;
    if (j is not null)
    {
        Console.WriteLine(j);
    }
    else
    {
        Console.WriteLine("Could not convert " + val.ToString());
    }
}
class Animal
{
    public void Eat() => Console.WriteLine("Eating.");
    public override string ToString() => "I am an animal.";
}
class Mammal : Animal { }
class Giraffe : Mammal { }

class SuperNova { }

이 코드를 패턴 일치 코드와 비교하여 볼 수 있듯이 패턴 일치 구문은 테스트와 할당을 단일 문으로 결합하여 보다 강력한 기능을 제공합니다. 가능하면 패턴 일치 구문을 사용합니다.