Je suis assez confus entre certains concepts de POO: virtual
, override
, new
et sealed override
. Quelqu'un peut-il expliquer les différences?
Je suis assez clair que si la méthode de la classe dérivée doit être utilisée, on peut utiliser le mot clé override
pour que la méthode de la classe de base soit remplacée par la classe dérivée. Mais je ne suis pas sûr de new
, et sealed override
.
Le mot clé virtual permet de modifier une déclaration de méthode, de propriété, d'indexeur ou d'événement et d'autoriser son remplacement dans une classe dérivée. Par exemple, cette classe peut être remplacée par toute classe qui en hérite: Utilisez le modificateur new pour masquer explicitement un membre hérité d'une classe de base. Pour masquer un membre hérité, déclarez-le dans la classe dérivée en utilisant le même nom et modifiez-le avec le modificateur new.
Tout cela a à voir avec le polymorphisme. Lorsqu'une méthode virtuelle est appelée sur une référence, le type réel de l'objet référencé par la référence est utilisé pour décider de l'implémentation de la méthode à utiliser. Lorsqu'une méthode d'une classe de base est substituée dans une classe dérivée, la version de la classe dérivée est utilisée, même si le code appelant ne "savait" pas que l'objet était une instance de la classe dérivée. Par exemple:
public class Base
{
public virtual void SomeMethod()
{
}
}
public class Derived : Base
{
public override void SomeMethod()
{
}
}
...
Base d = new Derived();
d.SomeMethod();
finira par appeler Derived.SomeMethod si cela remplace Base.SomeMethod.
Maintenant, si vous utilisez le mot-clé nouvea au lieu de substitution, la méthode de la classe dérivée ne remplace pas la méthode de la classe de base, elle la cache simplement. Dans ce cas, codez comme ceci:
public class Base
{
public virtual void SomeOtherMethod()
{
}
}
public class Derived : Base
{
public new void SomeOtherMethod()
{
}
}
...
Base b = new Derived();
Derived d = new Derived();
b.SomeOtherMethod();
d.SomeOtherMethod();
Appelera d'abord Base.SomeOtherMethod, puis Derived.SomeOtherMethod. Il s’agit en réalité de deux méthodes entièrement distinctes qui portent le même nom, plutôt que la méthode dérivée remplaçant la méthode de base.
Si vous ne spécifiez ni nouvelle ni substitution, la sortie obtenue est la même que si vous spécifiiez nouvelle, mais vous obtiendrez également un avertissement pour le compilateur (car vous ne savez peut-être pas que vous masquez une méthode dans la classe de base. méthode, ou bien vous avez peut-être voulu la remplacer et vous avez tout simplement oublié d’inclure le mot clé).
Une déclaration de propriété de substitution peut inclure le modificateur scellé. L'utilisation de ce modificateur empêche une classe dérivée de surcharger la propriété. Les accesseurs d'une propriété scellée sont également scellés.
Toute méthode peut être remplacée (= virtual
) ou non. La décision est prise par celui qui définit la méthode:
class Person
{
// this one is not overridable (not virtual)
public String GetPersonType()
{
return "person";
}
// this one is overridable (virtual)
public virtual String GetName()
{
return "generic name";
}
}
Maintenant, vous pouvez remplacer les méthodes qui peuvent être remplacées:
class Friend : Person
{
public Friend() : this("generic name") { }
public Friend(String name)
{
this._name = name;
}
// override Person.GetName:
public override String GetName()
{
return _name;
}
}
Mais vous ne pouvez pas remplacer la méthode GetPersonType
car elle n'est pas virtuelle.
Créons deux instances de ces classes:
Person person = new Person();
Friend friend = new Friend("Onotole");
Lorsque la méthode non virtuelle GetPersonType
est appelée par Fiend
instance, il s’agit en fait de Person.GetPersonType
que l'on appelle:
Console.WriteLine(friend.GetPersonType()); // "person"
Lorsque la méthode virtuelle GetName
est appelée par Friend
instance, il s’agit de Friend.GetName
que l'on appelle:
Console.WriteLine(friend.GetName()); // "Onotole"
Lorsque la méthode virtuelle GetName
est appelée par Person
instance, il s’agit de Person.GetName
que l'on appelle:
Console.WriteLine(person.GetName()); // "generic name"
Lorsque la méthode non virtuelle est appelée, le corps de la méthode n'est pas recherché - le compilateur connaît déjà la méthode à appeler. Tandis qu'avec les méthodes virtuelles, le compilateur ne sait pas lequel appeler, et il est recherché lors de l'exécution dans la hiérarchie des classes de bas en haut, en commençant par le type d'instance sur lequel la méthode est appelée: for friend.GetName
il semble commencer à la classe Friend
et le trouve tout de suite, pour person.GetName
classe il commence à Person
et le trouve à cet endroit.
Parfois, vous créez une sous-classe, substituez une méthode virtuelle et vous ne voulez plus de substitutions dans la hiérarchie - vous utilisez sealed override
pour cela (en disant que vous êtes le dernier à surcharger la méthode):
class Mike : Friend
{
public sealed override String GetName()
{
return "Mike";
}
}
Mais parfois, votre ami Mike décide de changer de sexe et donc de s'appeler Alice. Vous pouvez modifier le code d'origine ou remplacer la sous-classe Mike:
class Alice : Mike
{
public new String GetName()
{
return "Alice";
}
}
Ici, vous créez une méthode complètement différente avec le même nom (maintenant vous en avez deux). Quelle méthode et quand s'appelle? Cela dépend comment vous l'appelez:
Alice alice = new Alice();
Console.WriteLine(alice.GetName()); // the new method is called, printing "Alice"
Console.WriteLine(((Mike)alice).GetName()); // the method hidden by new is called, printing "Mike"
Lorsque vous appelez cela du point de vue de Alice
, vous appelez Alice.GetName
, à partir de Mike
- vous appelez Mike.GetName
. Aucune recherche d'exécution n'est faite ici, car les deux méthodes ne sont pas virtuelles.
Vous pouvez toujours créer des méthodes new
, que les méthodes que vous cachez soient virtuelles ou non.
Ceci s’applique également aux propriétés et aux événements - ils sont représentés sous la forme de méthodes.
Par défaut, une méthode ne peut être remplacée dans une classe dérivée que si elle est déclarée virtual
ou abstract
. virtual
signifie recherchez les nouvelles implémentations avant d'appeler et abstract
signifie la même chose, mais son remplacement est garanti dans toutes les classes dérivées. De plus, aucune implémentation n'est nécessaire dans la classe de base car elle sera redéfinie ailleurs.
L'exception à ce qui précède est le modificateur new
. Une méthode non déclarée virtual
ou abstract
peut être redéfinie avec le modificateur new
dans une classe dérivée. Lorsque la méthode est appelée dans la classe de base, la méthode de base est exécutée et lorsqu'elle est appelée dans la classe dérivée, la nouvelle méthode est exécutée. Tous les new
mots-clés vous permettent de faire est d'avoir deux méthodes avec le même nom dans une hiérarchie de classes.
Enfin, un modificateur sealed
rompt la chaîne de méthodes virtual
et ne les redéfinit plus. Ce n'est pas souvent utilisé, mais l'option est là. Cela a plus de sens avec une chaîne de 3 classes chacune dérivant de la précédente
A -> B -> C
si A
a une méthode virtual
ou abstract
, c'est-à-dire que overridden
dans B
, alors il peut également empêcher C
de le changer à nouveau en le déclarant sealed
dans B
.
sealed
est également utilisé dans classes
, et vous rencontrerez couramment ce mot clé.
J'espère que ça aide.
public class Base
{
public virtual void SomeMethod()
{
Console.WriteLine("B");
}
}
public class Derived : Base
{
//Same method is written 3 times with different keywords to explain different behaviors.
//This one is Simple method
public void SomeMethod()
{
Console.WriteLine("D");
}
//This method has 'new' keyword
public new void SomeMethod()
{
Console.WriteLine("D");
}
//This method has 'override' keyword
public override void SomeMethod()
{
Console.WriteLine("D");
}
}
Maintenant première chose première
Base b=new Base();
Derived d=new Derived();
b.SomeMethod(); //will always write B
d.SomeMethod(); //will always write D
Maintenant, les mots clés sont tous sur le polymorphisme
Base b = new Derived();
virtual
dans la classe de base et remplacer dans Derived
donnera D (polymorphisme).override
sans virtual
dans Base
donnera une erreur.virtual
écrira 'B' avec avertissement (car aucun polymorphisme n'est effectué).new
avant cette méthode simple dans Derived
.new
mot-clé est une autre histoire, il cache simplement l'avertissement qui indique que la propriété du même nom est présente dans la classe de base.virtual
ou new
les deux sont identiques sauf nouveau modificateur
new
et override
ne peuvent pas être utilisés avant la même méthode ou propriété.
sealed
avant toute classe ou méthode, verrouillez-la pour qu'elle soit utilisée dans la classe dérivée et donne une erreur lors de la compilation.