Veilig casten met behulp van patroonkoppeling en de is en als operators

Omdat objecten polymorf zijn, is het mogelijk dat een variabele van een basisklassetype een afgeleid type bevat. Als u toegang wilt krijgen tot de leden van het afgeleide type, moet u de waarde terugzetten naar het afgeleide type. Echter, een cast creëert het risico van het gooien van een InvalidCastException. C# biedt patroonkoppelingsinstructies die alleen voorwaardelijk een cast uitvoeren wanneer deze slaagt. C# biedt ook de is en als operators om te testen of een waarde van een bepaald type is.

In het volgende voorbeeld ziet u hoe u de patroonkoppelingsinstructie is gebruikt:

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

In het voorgaande voorbeeld ziet u enkele functies van de syntaxis van patroonkoppeling. De if (a is Mammal m) instructie combineert de test met een initialisatietoewijzing. De toewijzing vindt alleen plaats wanneer de test slaagt. De variabele m bevindt zich alleen in het bereik van de ingesloten if instructie waaraan deze is toegewezen. U hebt later geen toegang tot m dezelfde methode. In het voorgaande voorbeeld ziet u ook hoe u de as operator gebruikt om een object te converteren naar een opgegeven type.

U kunt ook dezelfde syntaxis gebruiken om te testen of een type null-waarde een waarde heeft, zoals wordt weergegeven in het volgende voorbeeld:

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

In het voorgaande voorbeeld ziet u andere functies van patroonkoppeling die moeten worden gebruikt met conversies. U kunt een variabele voor het null-patroon testen door specifiek op de null waarde te controleren. Wanneer de runtimewaarde van de variabele is, wordt nulleen is instructiecontrole voor een type altijd geretourneerd false. De patroonkoppelingsinstructie is staat geen null-waardetype toe, zoals int? of Nullable<int>, maar u kunt testen op elk ander waardetype. De is patronen uit het voorgaande voorbeeld zijn niet beperkt tot de typen null-waarden. U kunt deze patronen ook gebruiken om te testen of een variabele van een verwijzingstype een waarde heeft of wel null.

In het voorgaande voorbeeld ziet u ook hoe u het typepatroon gebruikt in een switch instructie waarin de variabele een van de vele verschillende typen kan zijn.

Als u wilt testen of een variabele een bepaald type is, maar deze niet toewijst aan een nieuwe variabele, kunt u de is en as operators gebruiken voor verwijzingstypen en null-waardetypen. De volgende code laat zien hoe u de is en as instructies gebruikt die deel uitmaken van de C#-taal voordat patroonkoppeling is geïntroduceerd om te testen of een variabele van een bepaald type is:

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

Zoals u kunt zien door deze code te vergelijken met de patroonkoppelingscode, biedt de syntaxis voor patroonkoppeling krachtigere functies door de test en de toewijzing in één instructie te combineren. Gebruik waar mogelijk de syntaxis van de patroonkoppeling.