web-dev-qa-db-fra.com

La différence entre virtual, override, new et scellé

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.

76
Romil N

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.

101
CharithJ

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.

35
Loki Kriasus

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.

17
ja72
 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();
  1. Utiliser virtual dans la classe de base et remplacer dans Derived donnera D (polymorphisme).
  2. Utiliser override sans virtual dans Base donnera une erreur.
  3. De même, écrire une méthode (sans substitution) avec virtual écrira 'B' avec avertissement (car aucun polymorphisme n'est effectué).
  4. Pour masquer cet avertissement, écrivez new avant cette méthode simple dans Derived.
  5. 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.
  6. virtual ou new les deux sont identiques sauf nouveau modificateur

  7. new et override ne peuvent pas être utilisés avant la même méthode ou propriété.

  8. 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.
8
Charlie