C# Language virtuel, remplacement, nouveau


Exemple

virtuel et remplacement

Le mot-clé virtual permet à une méthode, une propriété, un indexeur ou un événement d'être remplacés par des classes dérivées et présente un comportement polymorphe. (Les membres sont non virtuels par défaut en C #)

public class BaseClass
{
    public virtual void Foo()
    {
        Console.WriteLine("Foo from BaseClass");
    }
}

Afin de remplacer un membre, le mot clé override est utilisé dans les classes dérivées. (Notez que la signature des membres doit être identique)

public class DerivedClass: BaseClass
{
    public override void Foo()
    {
        Console.WriteLine("Foo from DerivedClass");
    }
}

Le comportement polymorphe des membres virtuels signifie que, lorsqu’il est appelé, le membre en cours d’exécution est déterminé lors de l’exécution et non lors de la compilation. Le membre dominant dans la classe la plus dérivée de l'objet particulier est une instance exécutée.

En bref, l'objet peut être déclaré de type BaseClass au moment de la compilation, mais si à l'exécution il s'agit d'une instance de DerivedClass le membre remplacé sera exécuté:

BaseClass obj1 = new BaseClass();
obj1.Foo(); //Outputs "Foo from BaseClass"

obj1 = new DerivedClass();
obj1.Foo(); //Outputs "Foo from DerivedClass"    

La substitution d'une méthode est facultative:

public class SecondDerivedClass: DerivedClass {}

var obj1 = new SecondDerivedClass();
obj1.Foo(); //Outputs "Foo from DerivedClass"    

Nouveau

Étant donné que seuls les membres définis comme virtual sont remplaçables et polymorphes, une classe dérivée qui redéfinit un membre non virtuel peut entraîner des résultats inattendus.

public class BaseClass
{
    public void Foo()
    {
        Console.WriteLine("Foo from BaseClass");
    }
}

public class DerivedClass: BaseClass
{
    public void Foo()
    {
        Console.WriteLine("Foo from DerivedClass");
    }
}

BaseClass obj1 = new BaseClass();
obj1.Foo(); //Outputs "Foo from BaseClass"

obj1 = new DerivedClass();
obj1.Foo(); //Outputs "Foo from BaseClass" too!    

Lorsque cela se produit, le membre exécuté est toujours déterminé au moment de la compilation en fonction du type d'objet.

  • Si l'objet est déclaré de type BaseClass (même si à l'exécution est d'une classe dérivée), alors la méthode de BaseClass est exécutée
  • Si l'objet est déclaré de type DerivedClass alors la méthode de DerivedClass est exécutée.

Il s'agit généralement d'un accident (lorsqu'un membre est ajouté au type de base après qu'un membre identique ait été ajouté au type dérivé) et qu'un avertissement de compilation CS0108 est généré dans ces scénarios.

Si c'était intentionnel, le new mot-clé est utilisé pour supprimer l'avertissement du compilateur (et informez les autres développeurs de vos intentions!). le comportement reste le même, le new mot-clé supprime simplement l'avertissement du compilateur.

public class BaseClass
{
    public void Foo()
    {
        Console.WriteLine("Foo from BaseClass");
    }
}

public class DerivedClass: BaseClass
{
    public new void Foo()
    {
        Console.WriteLine("Foo from DerivedClass");
    }
}

BaseClass obj1 = new BaseClass();
obj1.Foo(); //Outputs "Foo from BaseClass"

obj1 = new DerivedClass();
obj1.Foo(); //Outputs "Foo from BaseClass" too! 

L'utilisation de la substitution n'est pas facultative

Contrairement au C ++, l'utilisation du mot clé override n'est pas facultative:

public class A
{
    public virtual void Foo()
    {
    }
}

public class B : A
{
    public void Foo() // Generates CS0108
    {
    }
}

L'exemple ci-dessus provoque également l'avertissement CS0108 , car B.Foo() ne A.Foo() pas automatiquement A.Foo() . Ajouter une override lorsque l'intention est de remplacer la classe de base et de provoquer un comportement polymorphe, d'ajouter de new lorsque vous souhaitez un comportement non polymorphe et de résoudre l'appel en utilisant le type statique. Ce dernier doit être utilisé avec prudence, car il peut entraîner une grave confusion.

Le code suivant entraîne même une erreur:

public class A
{
    public void Foo()
    {
    }
}

public class B : A
{
    public override void Foo() // Error: Nothing to override
    {
    }
}

Les classes dérivées peuvent introduire un polymorphisme

Le code suivant est parfaitement valide (bien que rare):

    public class A
    {
        public void Foo()
        {
            Console.WriteLine("A");
        }
    }

    public class B : A
    {
        public new virtual void Foo() 
        {
            Console.WriteLine("B");
        }
    }

Maintenant, tous les objets avec une référence statique de B (et ses dérivés) utilisent le polymorphisme pour résoudre Foo() , tandis que les références de A utilisent A.Foo() .

A a = new A();
a.Foo(); // Prints "A";
a = new B();
a.Foo(); // Prints "A";
B b = new B();
b.Foo(); // Prints "B";

Les méthodes virtuelles ne peuvent pas être privées

Le compilateur C # est strict en empêchant les constructions insensées. Les méthodes marquées comme virtual ne peuvent pas être privées. Parce qu'une méthode privée ne peut pas être vue à partir d'un type dérivé, elle ne peut pas non plus être écrasée. Cela ne compile pas:

public class A
{
    private virtual void Foo() // Error: virtual methods cannot be private
    {
    }
}