Vorgehensweise: Sicheres Umwandeln mit Musterabgleich und den Operatoren „is“ und „as“

Da Objekte polymorph sind, ist es möglich, dass eine Variable eines Basisklassentyps einen abgeleiteten Typ enthalten kann. Für den Zugriff auf die Instanzmember des abgeleiteten Typs ist es erforderlich, dass Sie den Wert wieder in den abgeleiteten Typ umwandeln. Allerdings entsteht durch eine Umwandlung das Risiko, eine InvalidCastException-Ausnahme auszulösen. C# stellt Musterabgleich-Anweisungen bereit, die eine Umwandlung unter der Bedingung ausführen, dass sie erfolgreich sein wird. C# bietet außerdem die Operatoren is und as, um zu testen, ob ein Wert einen bestimmten Typ aufweist.

Das folgende Beispiel zeigt die Verwendung der is-Anweisung zum Musterabgleich:

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

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");
    }
}
// Output:
// I am an animal.
// SuperNova 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 { }

Im vorhergehenden Beispiel werden einige Features der Syntax für den Musterabgleich veranschaulicht. Die if (a is Mammal m)-Anweisung kombiniert den Test mit einer Initialisierungszuweisung. Die Zuweisung tritt nur auf, wenn der Test erfolgreich ist. Die Variable m befindet sich nur im Bereich der eingebetteten if-Anweisung, dem sie zugewiesen wurde. Sie können in der gleichen Methode nicht später auf m zugreifen. Das vorstehende Beispiel zeigt auch, wie der as-Operator zum Konvertieren eines Objekts in einen bestimmten Typ verwendet wird.

Sie können mithilfe der gleichen Syntax testen, ob ein Nullable-Werttyp einen Wert aufweist, wie im folgenden Beispiel gezeigt:

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

Im vorhergehenden Beispiel werden weitere Features des Musterabgleichs veranschaulicht, die mit Konvertierungen verwendet werden können. Sie können eine Variable für das NULL-Muster prüfen, indem Sie spezifisch nach dem null-Wert sehen. Wenn der Laufzeitwert der Variable null ist, gibt eine is-Anweisung, die nach einem Typ prüft, immer false zurück. Die is-Anweisung für den Musterabgleich lässt keinen Nullable-Typ für den Wert zu, z.B. int? oder Nullable<int>, jedoch können Sie nach jedem anderen Werttyp prüfen. Die is-Muster aus dem vorherigen Beispiel sind nicht auf die Nullable-Werttypen beschränkt. Sie können diese Muster auch verwenden, um zu testen, ob eine Variable eines Verweistyps über einen Wert oder verfügt oder null ist.

Im vorangegangenen Beispiel wird auch gezeigt, wie Sie das Typmuster in einer switch-Anweisung verwenden, bei der die Variable einen von vielen verschiedenen Typen aufweisen kann.

Wenn Sie testen möchten, ob eine Variable einen bestimmten Typ aufweist, Sie aber keine neue Variable zuweisen möchten, können Sie die Operatoren is und as für Verweistypen und Nullable-Werttypen verwenden. Im folgenden Code wird veranschaulicht, wie Sie die Anweisungen is und as verwenden, die Teil der C#-Sprache waren, bevor der Musterabgleich eingeführt wurde, um zu überprüfen, ob eine Variable einen bestimmten Typ aufweist:

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

Wie Sie durch Vergleichen dieses Codes mit dem Code für den Musterabgleich sehen können, stellt die Syntax für den Musterabgleich robustere Features bereit, indem der Test und die Zuweisung in einer einzelnen Anweisung kombiniert werden. Verwenden Sie nach Möglichkeit die Syntax für den Musterabgleich.