Si vous connaissez déjà new
et override
dans les grandes lignes, aller directement au résumé des cas possibles pour vérifier si vous maîtrisez tous les cas de figures.
Même si les mots clés new
et override
n’ont, à priori, rien à voir, d’une façon générale ils modifient le comportement de membres d’une classe dans le cadre d’un héritage. Même si le cadre général d’utilisation de ces mots clés est connu, quelques cas particuliers peuvent mener à des comportements inattendus.
Override
override
est le mot clé permettant de surcharger un membre public
ou protected
dans une classe dérivée.
Héritage simple
Pour effectuer une surcharge, il faut que le membre de la classe de base soit virtuel c’est-à-dire qu’il utilise le mot clé virtual
. virtual
autorise, ainsi, les surcharges éventuelles dans les classes dérivées.
Par exemple, si on définit la classe de base:
public class BaseClass
{
private int innerValue = 7;
public virtual int InnerValue
{
get { return this.innerValue; }
}
public int ComputeValue(int value)
{
return this.AddToInnerValue(value);
}
protected virtual int AddToInnerValue(int value)
{
return value + this.innerValue;
}
}
La propriété InnerValue
et la méthode AddToInnerValue()
sont virtuelles et donc surchargeable dans une classe dérivée:
public class DerivedClass : BaseClass
{
public override int InnerValue
{
get { return 4; }
}
protected override int AddToInnerValue(int value)
{
return value + 9;
}
}
override
permet de modifier la définition d’un membre public
ou protected
dans la classe dérivée.
On peut faire appel à la classe de base en utilisant le mot clé base
.
Par exemple, dans la classe DerivedClass
, on peut appeler la méthode dans la classe BaseClass
en faisant:
protected override int AddToInnerValue(int value)
{
return base.AddToInnerValue(value);
}
Héritage d’une classe abstraite
override
s’utilise aussi pour déclarer un membre public
ou protected
dans une classe dérivée si ces membres sont abstraits dans la classe de base. Pour indiquer qu’un membre est abstrait dans la classe de base, il doit utiliser le mot clé abstract
.
Si une classe comporte au moins un membre abstrait, elle est déclarée abstraite et doit aussi être déclarée en utilisant le mot clé abstract
.
Par exemple, si on déclare la classe abstraite:
public abstract class BaseClass
{
public abstract int InnerValue { get; }
public int ComputeValue(int value)
{
return this.AddToInnerValue(value);
}
protected abstract int AddToInnerValue(int value);
}
On peut surcharger les membres abstraits en utilisant override
:
public class DerivedClass : BaseClass
{
public override int InnerValue
{
get { return 4; }
}
protected override int AddToInnerValue(int value)
{
return value + 9;
}
}
Dans la classe abstraite, les membres abstraits sont juste déclarés et n’ont pas d’implémentation. Si la classe dérivée n’est pas abstraite, il est obligatoire de définir tous les membres abstraits.
Si la classe dérivée est abstraite, il n’est pas obligatoire de définir une implémentation pour tous les membres abstraits.
New
new
n’est pas un opérateur d’héritage. Il permet d’indiquer au compilateur qu’on souhaite appeler un membre dans une classe dérivée de la même façon qu’un autre membre dans la classe de base.
Par exemple, on définit la classe de base suivante:
public class BaseClass
{
public int InnerValue(int value)
{
return value + 5;
}
}
Si on définit la classe dérivée avec le même nom de méthode:
public class DerivedClass : BaseClass
{
public int AddToInnerValue(int value)
{
return value + 9;
}
}
A la compilation, on aura le message de “warning”:
Warning 'DerivedClass.AddToInnerValue' hides inherited member 'BaseClass.AddToInnerValue'. Use the new keyword if hiding was intended.
Pour supprimer ce message de “warning”, il suffit d’utiliser le mot clé new
dans la méthode de la classe dérivée:
public class DerivedClass : BaseClass
{
public new int AddToInnerValue(int value)
{
return value + 9;
}
}
Le mot clé new
ne modifie pas le comportement d’héritage des classes. La méthode AddToInnerValue()
définit dans la classe de base BaseClass
n’est pas déclarée “virtuelle” donc la méthode AddToInnerValue()
dans la classe dérivée DerivedClass
ne surcharge pas la méthode dans la classe de base. Il s’agit d’une nouvelle méthode qui n’a rien à voir avec celle définit dans la classe de base. Ainsi dans tous les cas d’utilisation, c’est la méthode de la classe de base qui sera exécutée sauf si on n’utilise pas le polymorphisme.
Exemples d’utilisation de “new” et “override”
Utilisation de “override”
Si on définit les objets A, B
et I
de la façon suivante:
public interface I
{
void Method();
}
public class A : I
{
public virtual void Method()
{
Console.WriteLine("Exécuté dans A");
}
}
public class B : A
{
public override void Method()
{
Console.WriteLine("Exécuté dans B");
}
}
Différents cas d’utilisation des classes avec “override”
A instance1 = new A();
instance1.Method(); // Exécuté dans A
A instance2 = new B();
instance2.Method(); // Exécuté dans B
B instance3 = new B();
instance3.Method(); // Exécuté dans B
I instance4 = new A();
instance4.Method(); // Exécuté dans A
I instance5 = new B();
instance5.Method(); // Exécuté dans B
Dans le cas d’un héritage classique, override
permet de définir un comportement différent de la méthode dans le classe dérivée.
Si l’objet est du type de la classe dérivée, la méthode exécutée sera toujours celle de la classe dérivée quelque soit le type de la variable référençant l’objet.
Utilisation de “new”
Si on définit les objets A, B
et I
de la façon suivante:
public interface I
{
void Method();
}
public class A : I
{
public void Method()
{
Console.WriteLine("Exécuté dans A");
}
}
public class B : A
{
public new void Method()
{
Console.WriteLine("Exécuté dans B");
}
}
Différents cas d’utilisation des classes avec “new”
A instance1 = new A();
instance1.Method(); // Exécuté dans A
A instance2 = new B();
instance2.Method(); // Exécuté dans A
B instance3 = new B();
instance3.Method(); // Exécuté dans B
I instance4 = new A();
instance4.Method(); // Exécuté dans A
I instance5 = new B();
instance5.Method(); // Exécuté dans A
new
ne permet pas de substituer le lien d’héritage entre les classes: mis à part le cas où on instancie la classe dérivée et qu’on l’utilise dans son type d’origine, dans tous les cas, c’est la méthode dans la classe de base qui sera exécutée.
La méthode exécutée sera toujours celle de la classe de base sauf si l’objet et la variable sont explicitement du type de la classe dérivée.
Résumé des cas possibles
Dans le tableau suivant, on peut voir tous les comportements possibles dans le cas d’utilisation des mots clés new
et override
:
Définition et instanciation des objets | Variable de la classe dérivée Instanciation de la classe dérivée |
Variable de la classe de base Instanciation de la classe dérivée |
Variable avec le type de la classe de base Instanciation de la classe de base |
Variable avec le type d’une interface Instanciation de la classe de base |
Variable avec le type d’une interface Instanciation de la classe dérivée |
---|---|---|---|---|---|
|
|
|
|
|
|
Pas de surchage:
|
Exécution dans la classe de base (A) | ||||
Pas d’utilisation d’opérateur:
|
Exécution dans la classe dérivée (B )Compilation avec “warning” |
Exécution dans la classe de base (A )Compilation avec “warning” |
|||
virtual dans la classe base:
|
Exécution dans la classe dérivée (B )Compilation avec “warning” |
Exécution dans la classe de base (A )Compilation avec “warning” |
|||
new dans la classe dérivée:
|
Exécution dans la classe dérivée (B ) |
Exécution dans la classe de base (A ) |
|||
|
Exécution dans la classe dérivée (B ) |
Exécution dans la classe de base (A ) |
|||
Surcharge classique:
|
Exécution dans la classe dérivée (B ) |
Exécution dans la classe dérivée (B ) |
Exécution dans la classe de base (A ) |
Exécution dans la classe de base (A ) |
Exécution dans la classe dérivée (B ) |