Sdílet prostřednictvím


Jak bezpečně přetypovat pomocí porovnávání vzorů a operátorů is a as

Vzhledem k tomu, že objekty jsou polymorfní, je možné, aby proměnná základního typu třídy držela odvozený typ. Pro přístup k členům instance odvozeného typu je nutné přetypovat hodnotu zpět na odvozený typ. Přetypování však vytváří riziko vyvolání InvalidCastException. Jazyk C# poskytuje příkazy porovnávání vzorů , které provádějí podmíněné přetypování pouze tehdy, když bude úspěšné. Jazyk C# také poskytuje operátory is a as, které testují, zda je hodnota určitého typu.

Následující příklad ukazuje, jak použít příkaz porovnávání is vzorů:

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 { }

Předchozí ukázka ukazuje několik funkcí syntaxe porovnávání vzorů. Příkaz if (a is Mammal m) kombinuje test s inicializačním přiřazením. Přiřazení nastane pouze v případě, že test proběhne úspěšně. Proměnná m je v rozsahu pouze ve vloženém if příkazu, kde byla přiřazena. Nemůžete získat přístup k m později ve stejné metodě. Předchozí příklad také ukazuje, jak pomocí operátoruas převést objekt na zadaný typ.

Stejnou syntaxi můžete použít také k ověření, zda má nulovatelný typ hodnoty nějakou hodnotu, jak ukazuje následující příklad:

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;
    }
}

Předchozí ukázka ověřuje další funkce porovnávání vzorů použitelné s převody. Proměnnou pro vzor null můžete otestovat tak, že zkontrolujete konkrétní null hodnotu. Pokud je hodnota proměnné za běhu null, příkaz is, který ověřuje typ, vždy vrátí false. Příkaz porovnávání is vzorů neumožňuje typ hodnoty null, například int? nebo Nullable<int>, ale můžete testovat pro jakýkoli jiný typ hodnoty. Vzory is z předchozího příkladu nejsou omezeny na typy hodnot s možnou hodnotou null. Tyto vzory můžete použít také k otestování, zda proměnná typu odkazu má hodnotu nebo je null.

Předchozí ukázka také ukazuje, jak použít vzor typu v switch příkazu, kde proměnná může být jedním z mnoha různých typů.

Pokud chcete otestovat, jestli je proměnná daným typem, ale nepřiřazuje se k nové proměnné, můžete použít operátory is a operátory as pro odkazové typy a typy hodnot s možnou hodnotou null. Následující kód ukazuje, jak použít is a as příkazy, které byly součástí jazyka C#, než bylo zavedeno porovnávání vzorů k testování, pokud je proměnná daného typu:

// 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 { }

Jak můžete vidět porovnáním tohoto kódu s kódem porovnávání vzorů, syntaxe porovnávání vzorů poskytuje robustnější funkce kombinací testu a přiřazení v jednom příkazu. Kdykoli je to možné, použijte syntaxi porovnávání vzorů.