Versioning avec les mots clés override et new (Guide de programmation C#)

Le langage C# est conçu de telle sorte que la gestion de version entre les classes de base et les classes dérivées dans différentes bibliothèques puisse évoluer et préserver une compatibilité descendante. Cela signifie, par exemple, que l’introduction dans une classe de base d’un nouveau membre portant le même nom qu’un membre dans une classe dérivée est totalement prise en charge par C# et n’engendre pas de comportement imprévisible. Cela signifie également qu'une classe doit indiquer de façon explicite si une méthode est conçue pour se substituer à une méthode héritée ou s'il s'agit d'une nouvelle méthode qui masque une méthode héritée portant un nom similaire.

Dans C#, les classes dérivées peuvent contenir des méthodes portant le même nom que des méthodes de classe de base.

  • Si la méthode dans la classe dérivée n’est pas précédée des mots clés new ou override, le compilateur émet un avertissement et la méthode se comporte comme si le mot clé new était présent.

  • Si la méthode dans la classe dérivée est précédée du mot clé new, la méthode est définie comme étant indépendante de la méthode dans la classe de base.

  • Si la méthode dans la classe dérivée est précédée du mot clé override, les objets de la classe dérivée appelleront cette méthode plutôt que la méthode de la classe de base.

  • Pour appliquer le mot clé override à la méthode de la classe dérivée, la méthode de la classe de base doit être définie comme virtuelle.

  • La méthode de la classe de base peut être appelée à partir de la classe dérivée à l’aide du mot clé base.

  • Les mots clés override, virtual et new peuvent également être appliqués aux propriétés, indexeurs et événements.

Par défaut, les méthodes C# ne sont pas virtuelles. Si une méthode est déclarée comme virtuelle, toute classe héritant de la méthode peut implémenter sa propre version. Pour créer une méthode virtuelle, le modificateur virtual est utilisé dans la déclaration de méthode de la classe de base. La classe dérivée peut ensuite remplacer la méthode virtuelle de base avec le mot clé override ou masquer la méthode virtuelle dans la classe de base avec le mot clé new. Si ni le mot clé override ni le mot clé new ne sont spécifiés, le compilateur émettra un avertissement et la méthode dans la classe dérivée masquera la méthode dans la classe de base.

Pour illustrer ceci dans un exemple pratique, supposons un instant que la Société A ait créé une classe nommée GraphicsClass que votre programme utilise. Les éléments suivants représentent GraphicsClass :

class GraphicsClass
{
    public virtual void DrawLine() { }
    public virtual void DrawPoint() { }
}

Votre société utilise cette classe, et vous l’utilisez pour dériver votre propre classe, en ajoutant une nouvelle méthode :

class YourDerivedGraphicsClass : GraphicsClass
{
    public void DrawRectangle() { }
}

Votre application est utilisée sans problèmes, jusqu’à ce que la Société A publie une nouvelle version de GraphicsClass, qui ressemble au code suivant :

class GraphicsClass
{
    public virtual void DrawLine() { }
    public virtual void DrawPoint() { }
    public virtual void DrawRectangle() { }
}

La nouvelle version de GraphicsClass contient maintenant une méthode nommée DrawRectangle. Initialement, rien ne se produit. La nouvelle version est encore compatible au format binaire avec l’ancienne version. Tous les logiciels que vous avez déployés continueront de fonctionner, même si la nouvelle classe est installée sur ces systèmes informatiques. Tout appel existant à la méthode DrawRectangle continuera de faire référence à votre version, dans votre classe dérivée.

Toutefois, dès que vous recompilerez votre application à l’aide de la nouvelle version de GraphicsClass, vous recevrez un avertissement du compilateur, CS0108. Cet avertissement vous informe que vous devez envisager la façon dont vous souhaitez que votre méthode DrawRectangle se comporte dans votre application.

Si vous souhaitez que votre méthode remplace la nouvelle méthode de la classe de base, utilisez le mot clé override :

class YourDerivedGraphicsClass : GraphicsClass
{
    public override void DrawRectangle() { }
}

Le mot clé override garantit que tous les objets dérivés de YourDerivedGraphicsClass utiliseront la version de classe dérivée de DrawRectangle. Les objets dérivés de YourDerivedGraphicsClass peuvent toujours accéder à la version de la classe de base de DrawRectangle à l’aide du mot clé base :

base.DrawRectangle();

Si vous ne souhaitez pas que votre méthode se substitue à la nouvelle méthode de la classe de base, prenez en compte les considérations suivantes. Pour éviter toute confusion entre les deux méthodes, vous pouvez renommer votre méthode. Ceci peut prendre du temps et constituer un risque d’erreur ou bien ne pas être tout simplement pratique dans certains cas. Toutefois, si votre projet est relativement petit, vous pouvez utiliser les options de refactorisation de Visual Studio pour renommer la méthode. Pour plus d’informations, consultez Refactorisation des classes et des types (Concepteur de classes).

Sinon, vous pouvez éviter de recevoir l’avertissement à l’aide du mot clé new dans la définition de votre classe dérivée :

class YourDerivedGraphicsClass : GraphicsClass
{
    public new void DrawRectangle() { }
}

L’utilisation du mot clé new indique au compilateur que votre définition masque la définition qui est contenue dans la classe de base. Il s'agit du comportement par défaut.

Substitution et sélection de méthode

Lorsqu'une méthode est appelée sur une classe, le compilateur C# sélectionne la meilleure méthode à appeler si plusieurs méthodes sont compatibles avec l'appel, comme lorsque deux méthodes portent les mêmes noms et dont les paramètres sont compatibles avec les paramètres transmis. Les méthodes suivantes seraient compatibles :

public class Derived : Base
{
    public override void DoWork(int param) { }
    public void DoWork(double param) { }
}

Quand DoWork est appelée sur une instance de Derived, le compilateur C# essaie d’abord de rendre l’appel compatible avec les versions de DoWork déclarées à l’origine sur Derived. Les méthodes override ne sont pas considérées comme déclarées sur une classe ; il s’agit de nouvelles implémentations d’une méthode déclarée sur une classe de base. Le compilateur C# n’essaiera de faire correspondre l’appel à une méthode substituée portant le même nom et dotée de paramètres compatibles que s’il ne parvient pas à faire correspondre l’appel de méthode à une méthode d’origine sur Derived. Par exemple :

int val = 5;
Derived d = new Derived();
d.DoWork(val);  // Calls DoWork(double).

Étant donné que la variable val peut être convertie implicitement en un double, le compilateur C# appelle DoWork(double) au lieu de DoWork(int). Il existe deux façons d’éviter ceci. Premièrement, évitez de déclarer de nouvelles méthodes portant le même nom que des méthodes virtuelles. Deuxièmement, vous pouvez indiquer au compilateur C# d’appeler la méthode virtuelle en le faisant chercher dans la liste de méthodes de la classe de base par casting de l’instance Derived en Base. Étant donné que la méthode est virtuelle, l’implémentation de DoWork(int) sur Derived sera appelée. Par exemple :

((Base)d).DoWork(val);  // Calls DoWork(int) on Derived.

Pour obtenir d’autres exemples des mots clés new et override, consultez Savoir quand utiliser les mots clés override et new.

Voir aussi