Différence entre new et override


198

Vous vous demandez quelle est la différence entre les éléments suivants:

Cas 1: Classe de base

public void DoIt();

Cas 1: classe héritée

public new void DoIt();

Cas 2: Classe de base

public virtual void DoIt();

Cas 2: classe héritée

public override void DoIt();

Les cas 1 et 2 semblent avoir le même effet sur la base des tests que j'ai effectués. Y a-t-il une différence ou une manière préférée?


2
Duplicate de nombreuses questions, dont stackoverflow.com/questions/159978/…
Jon Skeet

Réponses:


267

Le modificateur de remplacement peut être utilisé sur des méthodes virtuelles et doit être utilisé sur des méthodes abstraites. Cela indique au compilateur d'utiliser la dernière implémentation définie d'une méthode. Même si la méthode est appelée sur une référence à la classe de base, elle utilisera l'implémentation en la remplaçant.

public class Base
{
    public virtual void DoIt()
    {
    }
}

public class Derived : Base
{
    public override void DoIt()
    {
    }
}

Base b = new Derived();
b.DoIt();                      // Calls Derived.DoIt

appellera Derived.DoItsi cela remplace Base.DoIt.

Le nouveau modificateur demande au compilateur d'utiliser votre implémentation de classe enfant au lieu de l'implémentation de classe parent. Tout code qui ne fait pas référence à votre classe, mais la classe parent utilisera l'implémentation de la classe parent.

public class Base
{
    public virtual void DoIt()
    {
    }
}

public class Derived : Base
{
    public new void DoIt()
    {
    }
}

Base b = new Derived();
Derived d = new Derived();

b.DoIt();                      // Calls Base.DoIt
d.DoIt();                      // Calls Derived.DoIt

Appellera d'abord Base.DoIt, alors Derived.DoIt. Ce sont en fait deux méthodes entièrement distinctes qui portent le même nom, plutôt que la méthode dérivée qui remplace la méthode de base.

Source: blog Microsoft


5
This indicates for the compiler to use the last defined implementation of a method. comment trouver la dernière implémentation définie d'une méthode ??
AminM

5
Commencez par une classe concrète, vérifiez si elle a une implémentation de la méthode d'intérêt. Si c'est le cas, vous avez terminé. Si ce n'est pas le cas, faites un pas de plus dans la hiérarchie d'héritage, c'est-à-dire vérifiez si la super classe a la méthode qui vous intéresse. Continuez jusqu'à ce que vous ayez trouvé la méthode qui vous intéresse.
csoltenborn

2
Notez également que vous pouvez uniquement overrideune méthode lorsque la classe de base définit la méthode comme virtual. Le mot virtualest la classe de base en disant « Hé, quand je l' appelle cette méthode, il aurait pu pratiquement été remplacée par une mise en œuvre dérivée, donc je ne sais pas vraiment à l' avance ce que la mise en œuvre méthode que je vous appelle en fait à l' exécution. Ainsi virtualsignifie est un espace réservé pour une méthode. Cela implique que les méthodes qui ne sont pas marquées comme virtualne peuvent pas être remplacées. Mais vous pouvez remplacer toute méthode non virtuelle dans une classe dérivée avec le modificateur new, accessible uniquement au niveau dérivé.
Erik Bongers

177

virtuel : indique qu'une méthode peut être remplacée par un héritier

override : remplace la fonctionnalité d'une méthode virtuelle dans une classe de base, fournissant des fonctionnalités différentes.

nouveau : cache la méthode d'origine (qui n'a pas besoin d'être virtuelle), offrant des fonctionnalités différentes. Cela ne doit être utilisé que lorsque cela est absolument nécessaire.

Lorsque vous masquez une méthode, vous pouvez toujours accéder à la méthode d'origine en effectuant une conversion vers la classe de base. Ceci est utile dans certains scénarios, mais dangereux.


2
Pourquoi la conversion d'une méthode qui cache la méthode de base est-elle dangereuse? Ou voulez-vous dire que la coulée en général est dangereuse?
Mark

3
@Mark - un appelant peut ne pas être au courant de la mise en œuvre, provoquant une mauvaise utilisation accidentelle.
Jon B

Pouvez-vous utiliser overrideet / ou newsans virtualsur la méthode parent?
Aaron Franke

16

Dans le premier cas, vous cachez la définition dans la classe parente. Cela signifie qu'il ne sera invoqué que lorsque vous traitez l'objet en tant que classe enfant. Si vous convertissez la classe en son type parent, la méthode du parent sera invoquée. Dans la deuxième instance, la méthode est remplacée et sera invoquée, que l'objet soit converti en classe enfant ou parent.


7

essayez de suivre: (case1)

((BaseClass)(new InheritedClass())).DoIt()

Edit: virtual + override sont résolus lors de l'exécution (donc override remplace vraiment les méthodes virtuelles), tandis que new crée simplement une nouvelle méthode avec le même nom et cache l'ancienne, elle est résolue au moment de la compilation -> votre compilateur appellera la méthode il ' voit '


3

Dans le cas 1, si vous utilisez la méthode DoIt () de la classe héritée alors que le type est déclaré comme classe de base, vous verrez même l'action de la classe de base.

/* Results
Class1
Base1
Class2
Class2
*/
public abstract class Base1
{
    public void DoIt() { Console.WriteLine("Base1"); }
}
public  class Class1 : Base1 
{
    public new void DoIt() { Console.WriteLine("Class1"); }
}
public abstract class Base2
{
    public virtual void DoIt() { Console.WriteLine("Base2"); }
}
public class Class2 : Base2
{
    public override void DoIt() { Console.WriteLine("Class2"); }
}
static void Main(string[] args)
{
    var c1 = new Class1();
    c1.DoIt();
    ((Base1)c1).DoIt();

    var c2 = new Class2();
    c2.DoIt();
    ((Base2)c2).DoIt();
    Console.Read();
}

Pourriez-vous publier l'avertissement ou l'erreur que vous recevez. Ce code a bien fonctionné lorsque je l'ai initialement publié.
Matthew Whited

Tout cela doit être collé dans votre classe de point d'entrée (programme). Cela a été supprimé pour permettre une meilleure mise en forme sur ce site.
Matthew Whited

3

La différence entre les deux cas est que dans le cas 1, la DoItméthode de base n'est pas remplacée, juste masquée. Cela signifie que selon le type de variable dépend de la méthode qui sera appelée. Par exemple:

BaseClass instance1 = new SubClass();
instance1.DoIt(); // Calls base class DoIt method

SubClass instance2 = new SubClass();
instance2.DoIt(); // Calls sub class DoIt method

Cela peut être très déroutant et entraîner des comportements non attendus et doit être évité si possible. Ainsi, le moyen préféré serait le cas 2.


3
  • newsignifie respecter votre type REFERENCE (côté gauche de =), exécutant ainsi la méthode des types de référence. Si la méthode redéfinie n'a pas de newmot - clé, elle se comporte comme elle l'a fait. De plus, il est également appelé héritage non polymorphe . C'est-à-dire: «Je crée une toute nouvelle méthode dans la classe dérivée qui n'a absolument rien à voir avec les méthodes du même nom dans la classe de base.» - par ledit Whitaker
  • override, qui doit être utilisé avec le virtualmot clé dans sa classe de base, signifie respecter votre type OBJECT (côté droit de =), exécutant ainsi la méthode surchargée quel que soit le type de référence. De plus, il est également appelé héritage polymorphe .

Ma façon de garder à l'esprit les deux mots clés qu'ils sont opposés l'un à l'autre.

override: le virtualmot clé doit être défini pour remplacer la méthode. La méthode utilisant un overridemot clé qui, quel que soit le type de référence (référence de la classe de base ou classe dérivée), si elle est instanciée avec la classe de base, la méthode de la classe de base s'exécute. Sinon, la méthode des classes dérivées s'exécute.

new: si le mot-clé est utilisé par une méthode, contrairement au overridemot-clé, le type de référence est important. S'il est instancié avec une classe dérivée et que le type de référence est la classe de base, la méthode de la classe de base s'exécute. S'il est instancié avec une classe dérivée et que le type de référence est une classe dérivée, la méthode de la classe dérivée s'exécute. À savoir, c'est le contraste du overridemot - clé. En passant, si vous oubliez ou omettez d'ajouter un nouveau mot clé à la méthode, le compilateur se comporte par défaut lorsque le newmot clé est utilisé.

class A 
{
    public string Foo() 
    {
        return "A";
    }

    public virtual string Test()
    {
        return "base test";
    }
}

class B: A
{
    public new string Foo() 
    {
        return "B";
    }
}

class C: B 
{
    public string Foo() 
    {
        return "C";
    }

    public override string Test() {
        return "derived test";
    }
}

Appelez en principal:

A AClass = new B();
Console.WriteLine(AClass.Foo());
B BClass = new B();
Console.WriteLine(BClass.Foo());
B BClassWithC = new C();
Console.WriteLine(BClassWithC.Foo());

Console.WriteLine(AClass.Test());
Console.WriteLine(BClassWithC.Test());

Production:

A
B
B
base test
derived test

Nouvel exemple de code,

Jouez avec le code en commentant un par un.

class X
{
    protected internal /*virtual*/ void Method()
    {
        WriteLine("X");
    }
}
class Y : X
{
    protected internal /*override*/ void Method()
    {
        base.Method();
        WriteLine("Y");
    }
}
class Z : Y
{
    protected internal /*override*/ void Method()
    {
        base.Method();
        WriteLine("Z");
    }
}

class Programxyz
{
    private static void Main(string[] args)
    {
        X v = new Z();
        //Y v = new Z();
        //Z v = new Z();
        v.Method();
}

1

Si le mot clé overrideest utilisé dans la classe dérivée, il remplace la méthode parent.

Si le mot new- clé est utilisé dans la classe dérivée, dérivez la méthode masquée par la méthode parent.


1

J'avais la même question et c'est vraiment déroutant, vous devriez considérer que le remplacement et les nouveaux mots clés ne fonctionnent qu'avec des objets de type classe de base et de valeur de classe dérivée. Dans ce cas seulement, vous verrez l'effet du remplacement et du nouveau: Donc, si vous avez class Aet B, Bhérite de A, vous instanciez un objet comme celui-ci:

A a = new B();

Désormais, les méthodes d'appel prendront en compte son état. Override : signifie qu'il étend la fonction de la méthode, puis il utilise la méthode dans la classe dérivée, tandis que new indique au compilateur de masquer la méthode dans la classe dérivée et d'utiliser la méthode dans la classe de base à la place. Voici une très bonne vue de ce sujet:

https://msdn.microsoft.com/EN-US/library/ms173153%28v=VS.140,d=hv.2%29.aspx?f=255&MSPPError=-2147217396


1

L'article ci-dessous est dans vb.net mais je pense que l'explication sur les nouveaux remplacements vs est très facile à comprendre.

https://www.codeproject.com/articles/17477/the-dark-shadow-of-overrides

À un moment donné de l'article, il y a cette phrase:

En général, Shadows suppose que la fonction associée au type est invoquée, tandis que Overrides suppose que l'implémentation de l'objet est exécutée.

La réponse acceptée à cette question est parfaite, mais je pense que cet article fournit de bons exemples pour ajouter un meilleur sens aux différences entre ces deux mots clés.


1

De tous ceux-là, le nouveau est le plus déroutant. Grâce à l'expérimentation, le nouveau mot-clé revient à donner aux développeurs la possibilité de remplacer l'implémentation de classe héritée par l'implémentation de classe de base en définissant explicitement le type. C'est comme penser l'inverse.

Dans l'exemple ci-dessous, le résultat renverra "Résultat dérivé" jusqu'à ce que le type soit explicitement défini comme test BaseClass, alors seulement "Résultat de base" sera renvoyé.

class Program
{
    static void Main(string[] args)
    {
        var test = new DerivedClass();
        var result = test.DoSomething();
    }
}

class BaseClass
{
    public virtual string DoSomething()
    {
        return "Base result";
    }
}

class DerivedClass : BaseClass
{
    public new string DoSomething()
    {
        return "Derived result";
    }
}

3
Ajoutez votre commentaire si vous vous y opposez. Frapper et courir est si lâche.
utileBee

0

La différence fonctionnelle ne sera pas montrée dans ces tests:

BaseClass bc = new BaseClass();

bc.DoIt();

DerivedClass dc = new DerivedClass();

dc.ShowIt();

Dans cet exemple, le Doit qui est appelé est celui auquel vous vous attendez.

Pour voir la différence, vous devez procéder comme suit:

BaseClass obj = new DerivedClass();

obj.DoIt();

Vous verrez si vous exécutez ce test que dans le cas 1 (tel que vous l'avez défini), l' DoIt()entrée BaseClassest appelée, dans le cas 2 (telle que vous l'avez définie), l' DoIt()entrée DerivedClassest appelée.


-1

Dans le premier cas, il appellera la méthode dérivée de classe DoIt () parce que le nouveau mot-clé masque la méthode de classe de base DoIt ().

Dans le deuxième cas, il appellera overriden DoIt ()

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

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

public class C : A
{
    public override void DoIt()
    {
        Console.WriteLine("C::DoIt()");
    }
}

laissez créer une instance de ces classes

   A instanceA = new A();

    B instanceB = new B();
    C instanceC = new C();

    instanceA.DoIt(); //A::DoIt()
    instanceB.DoIt(); //B::DoIt()
    instanceC.DoIt(); //B::DoIt()

Tout est attendu ci-dessus. Laissez la valeur instanceB et instanceC à instanceA et appelez la méthode DoIt () et vérifiez le résultat.

    instanceA = instanceB;
    instanceA.DoIt(); //A::DoIt() calls DoIt method in class A

    instanceA = instanceC;
    instanceA.DoIt();//C::DoIt() calls DoIt method in class C because it was overriden in class C

instanceC.DoIt (); vous donnerait C :: DoIt (), pas B :: DoIt ()
BYS2

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.