Differenza tra nuovo e override


198

Mi chiedo quale sia la differenza tra i seguenti:

Caso 1: Classe di base

public void DoIt();

Caso 1: classe ereditata

public new void DoIt();

Caso 2: Classe di base

public virtual void DoIt();

Caso 2: classe ereditata

public override void DoIt();

Entrambi i casi 1 e 2 sembrano avere lo stesso effetto in base ai test che ho eseguito. C'è una differenza o un modo preferito?


2
Duplica di molte domande, incluso stackoverflow.com/questions/159978/…
Jon Skeet,

Risposte:


267

Il modificatore di override può essere utilizzato su metodi virtuali e deve essere utilizzato su metodi astratti. Ciò indica che il compilatore deve utilizzare l'ultima implementazione definita di un metodo. Anche se il metodo viene chiamato su un riferimento alla classe base, utilizzerà l'implementazione che lo sovrascrive.

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

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

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

chiamerà Derived.DoItse questo ha la precedenza Base.DoIt.

Il nuovo modificatore indica al compilatore di utilizzare l'implementazione della classe figlio anziché l'implementazione della classe padre. Qualsiasi codice che non fa riferimento alla tua classe ma alla classe genitore utilizzerà l'implementazione della classe genitore.

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

Prima chiamerà Base.DoIt, quindiDerived.DoIt . Sono effettivamente due metodi completamente separati che hanno lo stesso nome, piuttosto che il metodo derivato che sostituisce il metodo di base.

Fonte: blog Microsoft


5
This indicates for the compiler to use the last defined implementation of a method. come trovare l'ultima implementazione definita di un metodo ??
AminM,

5
Inizia da una classe concreta, controlla se ha un'implementazione del metodo di interesse. Se lo fa, il gioco è fatto. In caso contrario, fai un passo avanti nella gerarchia dell'ereditarietà, ovvero verifica se la superclasse ha il metodo di interesse. Continua fino a quando non hai trovato il metodo di interesse.
csoltenborn,

2
Si noti inoltre che è possibile solo overrideun metodo quando la classe base definisce il metodo come virtual. La parola virtualè la classe base che dice "Ehi, quando chiamo questo metodo, potrebbe praticamente essere stato sostituito da un'implementazione derivata, quindi non so davvero in anticipo quale metodo di implementazione sto effettivamente chiamando in fase di esecuzione. Quindi virtualsignifica che è un segnaposto per un metodo. Ciò implica che i metodi che non sono contrassegnati come virtualnon possono essere sovrascritti. Tuttavia, è possibile sostituire qualsiasi metodo non virtuale in una classe derivata con il modificatore new, accessibile solo a livello derivato.
Erik Bongers,

177

virtuale : indica che un metodo può essere sostituito da un erede

override : ignora la funzionalità di un metodo virtuale in una classe base, fornendo funzionalità diverse.

nuovo : nasconde il metodo originale (che non deve essere virtuale), fornendo funzionalità diverse. Questo dovrebbe essere usato solo dove è assolutamente necessario.

Quando nascondi un metodo, puoi comunque accedere al metodo originale eseguendo il casting nella classe base. Questo è utile in alcuni scenari, ma pericoloso.


2
Perché lanciare un metodo che nasconde il metodo di base è pericoloso? O stai insinuando che il casting in generale è pericoloso?
Segna il

3
@Mark: un chiamante potrebbe non essere a conoscenza dell'implementazione, causando un uso improprio accidentale.
Jon B,

Puoi usare overridee / o newsenza virtualil metodo genitore?
Aaron Franke,

16

Nel primo caso stai nascondendo la definizione nella classe genitore. Ciò significa che verrà invocato solo quando si ha a che fare con l'oggetto come classe figlio. Se si esegue il cast della classe sul suo tipo parent, verrà invocato il metodo parent. Nella seconda istanza, il metodo viene sovrascritto e verrà invocato indipendentemente dal fatto che l'oggetto venga trasmesso come classe figlio o parent.


7

prova a seguire: (case1)

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

Modifica: virtual + override vengono risolti in fase di esecuzione (quindi l'override sostituisce davvero i metodi virtuali), mentre i nuovi creano solo un nuovo metodo con lo stesso nome e nascondono il vecchio, si risolvono al momento della compilazione -> il compilatore chiamerà il metodo " vede


3

Nel caso 1 se hai usato chiama il metodo DoIt () della classe ereditata mentre il tipo è dichiarato come classe base vedrai anche l'azione della classe 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();
}

Potresti pubblicare l'avviso o l'errore che stai ricevendo. Questo codice ha funzionato bene quando l'ho pubblicato inizialmente.
Matthew Whited,

Tutto questo dovrebbe essere incollato all'interno della classe del punto di ingresso (Programma). È stato rimosso per consentire una migliore formattazione su questo sito.
Matthew Whited,

3

La differenza tra i due casi è che nel caso 1, il DoItmetodo di base non viene ignorato, ma solo nascosto. Ciò significa che, a seconda del tipo di variabile, dipende dal metodo che verrà chiamato. Per esempio:

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

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

Questo può essere davvero confuso e comporta comportamenti non previsti e, se possibile, dovrebbe essere evitato. Quindi il modo preferito sarebbe il caso 2.


3
  • newsignifica rispettare il tipo di RIFERIMENTO (lato sinistro di =), eseguendo quindi il metodo dei tipi di riferimento. Se il metodo ridefinito non ha una newparola chiave, si comporta come ha fatto. Inoltre, è noto anche come eredità non polimorfica . Cioè, "Sto creando un metodo nuovissimo nella classe derivata che non ha assolutamente nulla a che fare con alcun metodo con lo stesso nome nella classe base." - disse Whitaker
  • override, che deve essere utilizzato con la virtualparola chiave nella sua classe di base, significa rispettare il tipo di OGGETTO (lato destro di =), eseguendo quindi il metodo overriden indipendentemente dal tipo di riferimento. Inoltre, è noto anche come eredità polimorfica .

Il mio modo di tenere a mente entrambe le parole chiave sono opposte.

override: la virtualparola chiave deve essere definita per sovrascrivere il metodo. Il metodo utilizza una overrideparola chiave che, indipendentemente dal tipo di riferimento (riferimento della classe base o della classe derivata) se viene istanziata con la classe base, viene eseguito il metodo della classe base. Altrimenti, viene eseguito il metodo di classe derivata.

new: se la parola chiave viene utilizzata da un metodo, diversamente dalla overrideparola chiave, il tipo di riferimento è importante. Se viene istanziato con la classe derivata e il tipo di riferimento è la classe base, viene eseguito il metodo della classe base. Se viene istanziato con la classe derivata e il tipo di riferimento è la classe derivata, viene eseguito il metodo della classe derivata. Vale a dire, è il contrasto della overrideparola chiave. Di fatto, se si dimentica o si omette di aggiungere una nuova parola chiave al metodo, il compilatore si comporta di default quando newviene utilizzata la parola chiave.

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";
    }
}

Chiama in principale:

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());

Produzione:

A
B
B
base test
derived test

Nuovo esempio di codice,

Gioca con il codice commentando uno a uno.

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

Se la parola chiave overrideviene utilizzata nella classe derive, la sua sostituzione ha la precedenza sul metodo parent.

Se la parola chiave newviene utilizzata nella classe derivate, allora deriva il metodo nascosto dal metodo parent.


1

Ho avuto la stessa domanda ed è davvero confuso, dovresti considerare che l' override e le nuove parole chiave funzionano solo con oggetti di tipo base e valore della classe derivata. Solo in questo caso vedrai l'effetto di override e new: Quindi se hai class Ae B, Beredita da A, allora crei un'istanza di un oggetto come questo:

A a = new B();

Ora i metodi di chiamata prenderanno in considerazione il suo stato. Override : significa che estende la funzione del metodo, quindi utilizza il metodo nella classe derivata, mentre il nuovo indica al compilatore di nascondere il metodo nella classe derivata e di utilizzare invece il metodo nella classe base. Ecco un ottimo spettacolo per quell'argomento:

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


1

L'articolo che segue è in vb.net ma penso che la spiegazione delle nuove sostituzioni vs sia molto facile da capire.

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

Ad un certo punto dell'articolo, c'è questa frase:

In generale, Shadows presuppone che venga invocata la funzione associata al tipo, mentre Overrides presuppone che venga eseguita l'implementazione dell'oggetto.

La risposta accettata a questa domanda è perfetta, ma penso che questo articolo fornisca buoni esempi per aggiungere un significato migliore alle differenze tra queste due parole chiave.


1

Di tutti questi, il nuovo è il più confuso. Attraverso la sperimentazione, la nuova parola chiave è come dare agli sviluppatori l'opzione di sovrascrivere l'implementazione della classe ereditaria con l'implementazione della classe base definendo esplicitamente il tipo. È come pensare al contrario.

Nell'esempio seguente, il risultato restituirà "Risultato derivato" fino a quando il tipo non viene esplicitamente definito come test BaseClass, solo dopo verrà restituito "Risultato base".

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
Aggiungi il tuo commento se ti opponi. Hit and run è così codardo.
utileBee

0

La differenza funzionale non verrà mostrata in questi test:

BaseClass bc = new BaseClass();

bc.DoIt();

DerivedClass dc = new DerivedClass();

dc.ShowIt();

In questo esempio, il Doit che viene chiamato è quello che ti aspetti di essere chiamato.

Per vedere la differenza devi fare questo:

BaseClass obj = new DerivedClass();

obj.DoIt();

Si vedrà se si esegue quel test che, nel caso 1 (come definito esso), il DoIt()di BaseClassè chiamato, nel caso 2 (come definito esso), il DoIt()di DerivedClassè chiamato.


-1

Nel primo caso chiamerà il metodo DoIt () della classe derivata perché la nuova parola chiave nasconde il metodo DoIt () della classe base.

Nel secondo caso chiamerà 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()");
    }
}

lascia creare un'istanza di queste classi

   A instanceA = new A();

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

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

Tutto è previsto sopra. Consentire di impostare instanceB e instanceC su instanceA e chiamare il metodo DoIt () e verificare il risultato.

    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 (); ti darebbe C :: DoIt (), non B :: DoIt ()
BYS2

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.