Comment effectuer un cast en toute sécurité à l’aide de critères spéciaux et des opérateurs is et As

Dans la mesure où les objets sont polymorphes, une variable d’un type de classe de base peut contenir un type dérivé. Pour accéder aux membres d’instance du type dérivé, il est nécessaire de réeffectuer un cast de la valeur vers le type dérivé. Toutefois, un cast risque de lever InvalidCastException. C# fournit des instructions relatives aux critères spéciaux, qui effectuent un cast de manière conditionnelle, uniquement en cas de réussite. C# fournit également les opérateurs is et as pour tester l’appartenance d’une valeur à un certain type.

L’exemple suivant montre comment utiliser l’instruction de critères is spéciaux :

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

L’exemple précédent illustre quelques fonctionnalités de la syntaxe relative aux critères spéciaux. L' if (a is Mammal m) instruction associe le test à une assignation d’initialisation. L’assignation n’a lieu qu’en cas de réussite du test. La variable m se trouve uniquement dans l’étendue de l’instruction if incorporée, à laquelle elle a été assignée. Vous ne pouvez pas accéder m ultérieurement dans la même méthode. L’exemple précédent montre également comment utiliser l' opérateur pour convertir un objet en un type spécifié.

Vous pouvez également utiliser la même syntaxe pour le test si un type valeur Nullable a une valeur, comme illustré dans l’exemple suivant :

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

L’exemple précédent illustre d’autres fonctionnalités relatives aux critères spéciaux pour les conversions. Vous pouvez tester le modèle null d’une variable en vérifiant spécifiquement la valeur null. Quand la valeur runtime de la variable est null, une instruction is de vérification de type retourne toujours false. L’instruction is relative aux critères spéciaux n’autorise aucun type valeur Nullable, par exemple int? ou Nullable<int>. Toutefois, vous pouvez tester un autre type valeur. Les is modèles de l’exemple précédent ne sont pas limités aux types valeur Nullable. Vous pouvez également utiliser ces modèles pour tester si une variable d’un type référence a une valeur ou s’il s’agit de null .

L’exemple précédent montre également comment utiliser le modèle de type dans une switch instruction où la variable peut être l’un des nombreux types différents.

Si vous souhaitez tester si une variable est un type donné, mais ne pas l’assigner à une nouvelle variable, vous pouvez utiliser les is opérateurs et as pour les types référence et les types valeur Nullable. Le code suivant montre comment utiliser les instructions is et as, qui faisaient partie du langage C# avant l’introduction des critères spéciaux permettant de déterminer l’existence ou non d’un type donné pour une variable :

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

Comme vous pouvez le constater en comparant ce code avec le code utilisant les critères spéciaux, la syntaxe des critères spéciaux fournit des fonctionnalités plus robustes, car elle combine le test et l’assignation dans une seule instruction. Utilisez la syntaxe des critères spéciaux autant que possible.