C # - Utilisation virtuelle des mots clés + remplacement par rapport à nouveau


204

Quelles sont les différences entre déclarer une méthode dans un type de base " virtual" et la remplacer ensuite dans un type enfant à l'aide du overridemot clé " " par opposition à simplement utiliser le newmot clé " " lors de la déclaration de la méthode correspondante dans le type enfant?


3
MSDN indique que «l'utilisation newcrée un nouveau membre avec le même nom et provoque la dissimulation du membre d'origine, tout en override
étendant


Réponses:


181

Le mot-clé "new" ne remplace pas, il signifie une nouvelle méthode qui n'a rien à voir avec la méthode de classe de base.

public class Foo
{
     public bool DoSomething() { return false; }
}

public class Bar : Foo
{
     public new bool DoSomething() { return true; }
}

public class Test
{
    public static void Main ()
    {
        Foo test = new Bar ();
        Console.WriteLine (test.DoSomething ());
    }
}

Ceci est faux, si vous avez utilisé la substitution, cela aurait été imprimé vrai.

(Code de base tiré de Joseph Daigle)

Donc, si vous faites du vrai polymorphisme, VOUS DEVEZ TOUJOURS SUPPRIMER . Le seul endroit où vous devez utiliser "nouveau" est lorsque la méthode n'est liée en aucune façon à la version de la classe de base.


Vous devriez réviser votre code, j'avais rendu les méthodes statiques (ce qui est valable pour utiliser le "nouveau" mot-clé). Mais j'ai décidé qu'il était plus clair d'utiliser des méthodes d'instance.
Joseph Daigle

1
Merci, j'ai raté la partie "statique". Devrait prêter plus d'attention à l'avenir
albertein

1
Notez que la ligne Foo test = new Bar () est cruciale ici, le mot-clé new / override pour les métodes détermine quel métode est appelé lorsque vous placez une barre dans une variable Foo.
Thomas N

... c'est donc exactement la même chose que de ne pas avoir virtualde base et overridede dérivé? Pourquoi existe-t-il? Le code fonctionnera toujours même sans new- est-ce donc simplement de la lisibilité?
Don Cheadle

Votre réponse, cher monsieur, est assez vague. new et virtual-override font la même chose, la seule différence est que new HIDES la méthode dans la classe parent et override .. eh bien, la remplace. Bien sûr, il affiche false car votre objet de test est de type Foo, s'il est de type Bar, il s'imprime vrai. Un exemple assez délicat, cependant.
MrSilent

228

Je trouve toujours des choses comme ça plus faciles à comprendre avec des images:

Encore une fois, en prenant le code de Joseph Daigle,

public class Foo
{
     public /*virtual*/ bool DoSomething() { return false; }
}

public class Bar : Foo
{
     public /*override or new*/ bool DoSomething() { return true; }
}

Si vous appelez ensuite le code comme ceci:

Foo a = new Bar();
a.DoSomething();

REMARQUE: L'important est que notre objet est en fait un Bar, mais nous le stockons dans une variable de typeFoo (c'est similaire à le transtyper)

Le résultat sera alors le suivant, selon que vous avez utilisé virtual/ overrideou newlors de la déclaration de vos classes.

Image d'explication virtuelle / de remplacement


3
Merci .... mais pourriez-vous s'il vous plaît expliquer un peu la photo ci-dessus en ce qui concerne le casting que vous avez dit?
odiseh

Oups si j'ai oublié d'ajouter ces quelques lignes à ma précédente. commentaire: voulez-vous dire que virtuel / prioritaire et non-vital / nouveau est utilisé uniquement pour le concept de polymorphisme et que lorsque vous déclarez simplement une variable (sans utiliser de transtypage), cela ne signifie pas? Merci encore.
odiseh

cette réponse est exactement ce que je cherchais. Le seul problème que j'ai eu, c'est que les images flickr sont bloquées sur mon lieu de travail, j'ai donc dû y accéder via mon téléphone ...
mezoid

7
Un réel effort pour répondre à la question.
iMatoria

Je voterais si vous pouvez réparer l'image? Son bloqué derrière le pare-feu corp ...
Jeremy Thompson

43

Voici du code pour comprendre la différence de comportement des méthodes virtuelles et non virtuelles:

class A
{
    public void foo()
    {
        Console.WriteLine("A::foo()");
    }
    public virtual void bar()
    {
        Console.WriteLine("A::bar()");
    }
}

class B : A
{
    public new void foo()
    {
        Console.WriteLine("B::foo()");
    }
    public override void bar()
    {
        Console.WriteLine("B::bar()");
    }
}

class Program
{
    static int Main(string[] args)
    {
        B b = new B();
        A a = b;
        a.foo(); // Prints A::foo
        b.foo(); // Prints B::foo
        a.bar(); // Prints B::bar
        b.bar(); // Prints B::bar
        return 0;
    }
}

merci pour cela - mais pourquoi utiliser newpour "cacher" la méthode de base, alors que tout simplement ne pas utiliser le remplacement semble faire la même chose?
Don Cheadle

1
Je ne pense pas qu'il a été créé pour empêcher la classe de base d'être remplacée. Je pense qu'il a été créé pour éviter les conflits de noms car vous ne pouvez pas remplacer une méthode qui ne l'est pas virtualet le compilateur se plaindra s'il voit le même nom de fonction sur une classe "bloodline" sans virtualsignature
mr5

19

Le newmot clé crée en fait un membre complètement nouveau qui n'existe que sur ce type spécifique.

Par exemple

public class Foo
{
     public bool DoSomething() { return false; }
}

public class Bar : Foo
{
     public new bool DoSomething() { return true; }
}

La méthode existe sur les deux types. Lorsque vous utilisez la réflexion et obtenez les membres de type Bar, vous trouverez en fait 2 méthodes appelées DoSomething()qui se ressemblent exactement. En utilisant, newvous masquez efficacement l'implémentation dans la classe de base, de sorte que lorsque les classes dérivent de Bar(dans mon exemple) l'appel de méthode à base.DoSomething()va à Baret non Foo.


9

virtual / override indique au compilateur que les deux méthodes sont liées et que dans certaines circonstances, lorsque vous pensez que vous appelez la première méthode (virtuelle), il est en fait correct d'appeler la deuxième méthode (remplacée) à la place. C'est le fondement du polymorphisme.

(new SubClass() as BaseClass).VirtualFoo()

Appellera la méthode VirtualFoo () remplacée de la sous-classe.

new indique au compilateur que vous ajoutez une méthode à une classe dérivée portant le même nom qu'une méthode de la classe de base, mais qu'elles n'ont aucune relation les unes avec les autres.

(new SubClass() as BaseClass).NewBar()

Appellera la méthode NewBar () de BaseClass, alors que:

(new SubClass()).NewBar()

Appellera la méthode NewBar () de la sous-classe.


aime vraiment cette phrase "raconte le compilateur"
Mina Gabriel

"fondement du polymorphisme" est un autre dicton chic :)
Jeremy Thompson

8

Au-delà des détails techniques, je pense que l'utilisation de virtual / override communique beaucoup d'informations sémantiques sur la conception. Lorsque vous déclarez une méthode virtuelle, vous indiquez que vous vous attendez à ce que les classes d'implémentation veuillent fournir leurs propres implémentations non par défaut. L'omission de cela dans une classe de base, de même, déclare l'attente que la méthode par défaut devrait suffire pour toutes les classes d'implémentation. De même, on peut utiliser des déclarations abstraites pour forcer les classes d'implémentation à fournir leur propre implémentation. Encore une fois, je pense que cela communique beaucoup sur la façon dont le programmeur s'attend à ce que le code soit utilisé. Si j'écrivais à la fois les classes de base et d'implémentation et que je me retrouvais à utiliser de nouvelles, je repenserais sérieusement la décision de ne pas rendre la méthode virtuelle dans le parent et de déclarer mon intention spécifiquement.


Les explications techniques de la nouvelle vs contrepassation sont solides, mais cette réponse semble aider le plus pour guider les développeurs sur lesquels utiliser.
Sully

4

La différence entre le mot clé de remplacement et le nouveau mot clé est que le premier remplace la méthode et le dernier masque la méthode.

Consultez les liens suivants pour plus d'informations ...

MSDN et autres


3
  • newle mot clé est pour cacher. - signifie que vous cachez votre méthode lors de l'exécution. La sortie sera basée sur la méthode de classe de base.
  • overridepour passer outre. - signifie que vous invoquez votre méthode de classe dérivée avec la référence de la classe de base. La sortie sera basée sur la méthode de classe dérivée.

1

Ma version d'explication vient de l'utilisation des propriétés pour aider à comprendre les différences.

overrideest assez simple, non? Le type sous-jacent remplace celui du parent.

newest peut-être trompeur (pour moi c'était le cas). Les propriétés sont plus faciles à comprendre:

public class Foo
{
    public bool GetSomething => false;
}

public class Bar : Foo
{
    public new bool GetSomething => true;
}

public static void Main(string[] args)
{
    Foo foo = new Bar();
    Console.WriteLine(foo.GetSomething);

    Bar bar = new Bar();
    Console.WriteLine(bar.GetSomething);
}

En utilisant un débogueur, vous pouvez remarquer qu'il Foo fooa 2 GetSomething propriétés, car il a en fait 2 versions de la propriété, Foo's et Bar', et pour savoir laquelle utiliser, c # "choisit" la propriété pour le type actuel.

Si vous vouliez utiliser la version de la barre, vous auriez utilisé la substitution ou utiliser à la Foo fooplace.

Bar bara seulement 1 , car il veut un comportement complètement nouveau pour GetSomething.


0

Ne pas marquer une méthode avec quoi que ce soit signifie: Lier cette méthode en utilisant le type de compilation de l'objet, pas le type d'exécution (liaison statique).

Marquer une méthode avec des virtualmoyens: Liez cette méthode en utilisant le type d'exécution de l'objet, et non le type de temps de compilation (liaison dynamique).

Marquer une virtualméthode de classe de base avec overridedans une classe dérivée signifie: Il s'agit de la méthode à lier à l'aide du type d'exécution de l'objet (liaison dynamique).

Marquer une virtualméthode de classe de base avec newdans une classe dérivée signifie: Il s'agit d'une nouvelle méthode, qui n'a aucun rapport avec celle portant le même nom dans la classe de base et qui doit être liée à l'aide du type de temps de compilation de l'objet (liaison statique).

Ne pas marquer une virtualméthode de classe de base dans la classe dérivée signifie: Cette méthode est marquée comme new(liaison statique).

Marquer une méthode abstractsignifie: Cette méthode est virtuelle, mais je ne déclarerai pas de corps pour elle et sa classe est également abstraite (liaison dynamique).

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.