C #: utilizzo delle parole chiave virtuale + sostituzione rispetto a nuovo


204

Quali sono le differenze tra dichiarare un metodo in un tipo di base " virtual" e sovrascriverlo in un tipo figlio usando la overrideparola chiave " " invece di usare semplicemente la newparola chiave " " quando si dichiara il metodo corrispondente nel tipo figlio?


3
MSDN dice "L'utilizzo newcrea un nuovo membro con lo stesso nome e fa sì che il membro originale override
venga


Risposte:


181

La "nuova" parola chiave non sovrascrive, significa un nuovo metodo che non ha nulla a che fare con il metodo della classe 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 ());
    }
}

Questo stampa falso, se hai usato l'override sarebbe stampato vero.

(Codice di base preso da Joseph Daigle)

Quindi, se stai facendo un vero polimorfismo DOVREBBE SEMPRE SOVRACCARICARE . L'unico posto in cui è necessario utilizzare "nuovo" è quando il metodo non è correlato in alcun modo alla versione della classe base.


Dovresti rivedere il tuo codice, ho reso statici i metodi (che è valido per usare la "nuova" parola chiave). Ma ho deciso che era più chiaro usare i metodi di istanza.
Joseph Daigle,

1
Grazie, ho perso la parte "statica". Dovrebbe prestare maggiore attenzione al futuro
albertein,

1
Si noti che la riga Foo test = new Bar () è cruciale qui, la parola chiave new / override per i metodi determina quale metodo viene chiamato quando si inserisce una barra in una variabile Foo.
Thomas N,

... quindi è esattamente come non avere virtualnella base e overridenel derivato? Perché esiste? Il codice funzionerà ancora anche senza new- quindi è puramente solo leggibilità?
Don Cheadle,

La tua risposta caro signore è piuttosto vaga. new e virtual-override fanno la stessa cosa, l'unica differenza è che new HIDES il metodo nella classe parent e override .. beh, lo sovrascrive. Ovviamente stampa falso perché il tuo oggetto test è di tipo Foo, se fosse di tipo Bar, verrebbe stampato vero. Esempio piuttosto complicato, però.
MrSilent,

228

Trovo sempre cose come questa più facilmente comprensibili con le immagini:

Ancora una volta, prendendo il codice di Joseph Daigle,

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

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

Se poi chiami il codice in questo modo:

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

NOTA: L'importante è che il nostro oggetto sia effettivamente un Bar, ma lo stiamo memorizzando in una variabile di tipoFoo (è simile al cast)

Quindi il risultato sarà il seguente, a seconda che tu abbia usato virtual/ overrideo newquando dichiari le tue classi.

Immagine di spiegazione virtuale / override


3
Grazie .... ma potresti per favore spiegare un po 'di foto sopra riguardo al casting che hai detto?
Odiseh,

Oops Se ho dimenticato di aggiungere queste poche righe al mio precedente. commento: vuoi dire virtuale / override e non vitual / new sono usati solo per il concetto di polimorfismo e quando dichiari semplicemente una variabile (non usando il casting) non significano? Grazie ancora.
Odiseh,

questa risposta è esattamente quello che stavo cercando. L'unico problema che ho avuto è che le immagini di Flickr sono bloccate sul mio posto di lavoro, quindi ho dovuto accedervi tramite il mio telefono ...
Mezoid

7
Sforzo reale per rispondere alla domanda.
iMatoria,

Vorrei votare se riesci a correggere l'immagine? È bloccato dietro un firewall corp ...
Jeremy Thompson il

43

Ecco un po 'di codice per comprendere la differenza nel comportamento dei metodi virtuali e non virtuali:

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

grazie per questo - ma perché usare newper "nascondere" il metodo base, quando semplicemente non usare l'override sembra fare lo stesso?
Don Cheadle,

1
Non penso che sia stato creato per impedire che la classe base venga annullata. Penso che sia stato creato per evitare lo scontro tra nomi in quanto non è possibile ignorare un metodo che non lo è virtuale il compilatore si lamenterà se vede lo stesso nome di funzione su una "linea di sangue" di classe senza virtualfirma
mr5

19

La newparola chiave in realtà crea un membro completamente nuovo che esiste solo su quel tipo specifico.

Per esempio

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

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

Il metodo esiste su entrambi i tipi. Quando usi la riflessione e ottieni i membri del tipo Bar, troverai effettivamente 2 metodi chiamati DoSomething()che sembrano esattamente uguali. Usando newsi nasconde in modo efficace l'implementazione nella classe base, in modo che quando le classi derivano Bar(nel mio esempio) la chiamata del metodo base.DoSomething()vada Bare non vada Foo.


9

virtual / override dice al compilatore che i due metodi sono correlati e che in alcune circostanze quando penseresti di chiamare il primo metodo (virtuale) è invece corretto chiamare il secondo metodo (sovrascritto). Questo è il fondamento del polimorfismo.

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

Chiamerà il metodo VirtualFoo () ignorato della sottoclasse.

new indica al compilatore che si sta aggiungendo un metodo a una classe derivata con lo stesso nome di un metodo nella classe base, ma non hanno alcuna relazione reciproca.

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

Chiamerà il metodo NewBar () di BaseClass, mentre:

(new SubClass()).NewBar()

Chiamerà il metodo NewBar () della sottoclasse.


piace molto questa frase "racconta al compilatore"
Mina Gabriel,

"fondamento del polimorfismo" è un altro detto di classe :)
Jeremy Thompson,

8

Oltre ai dettagli tecnici, penso che l'utilizzo di virtual / override comunichi molte informazioni semantiche sul design. Quando si dichiara un metodo virtuale, si indica che ci si aspetta che le classi di implementazione possano voler fornire le proprie implementazioni non predefinite. Omettere questo in una classe base, allo stesso modo, dichiara l'aspettativa che il metodo predefinito dovrebbe essere sufficiente per tutte le classi di implementazione. Allo stesso modo, si possono usare dichiarazioni astratte per forzare le classi di implementazione a fornire la propria implementazione. Ancora una volta, penso che questo comunichi molto su come il programmatore si aspetta che il codice venga utilizzato. Se stavo scrivendo sia la base che le classi di implementazione e mi fossi trovato ad usare il nuovo, avrei seriamente ripensato la decisione di non rendere il metodo virtuale nel genitore e dichiarare il mio intento in modo specifico.


Le spiegazioni tecniche del nuovo override sono valide, ma questa risposta sembra aiutare di più a guidare gli sviluppatori su cui utilizzare.
Sully,

4

La differenza tra la parola chiave override e la nuova parola chiave è che la prima esegue la sostituzione del metodo e la successiva nasconde il metodo.

Dai un'occhiata ai link seguenti per ulteriori informazioni ...

MSDN e altri


3
  • newla parola chiave è per nascondere. - significa che stai nascondendo il tuo metodo in fase di esecuzione. L'output sarà basato sul metodo della classe base.
  • overrideper l'override. - significa che stai invocando il metodo della classe derivata con il riferimento della classe base. L'output sarà basato sul metodo della classe derivata.

1

La mia versione della spiegazione viene dall'uso delle proprietà per aiutare a capire le differenze.

overrideè abbastanza semplice, vero? Il tipo sottostante sovrascrive quello del genitore.

newè forse il fuorviante (per me lo era). Con le proprietà è più facile capire:

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

Usando un debugger puoi notare che Foo fooha 2 GetSomething proprietà, poiché in realtà ha 2 versioni della proprietà, Foo"s Bar" e "s", e per sapere quale usare, c # "seleziona" la proprietà per il tipo corrente.

Se avessi voluto usare la versione della barra, avresti usato la sostituzione o l'utilizzo Foo foo.

Bar barha solo 1 , in quanto vuole un comportamento completamente nuovo per GetSomething.


0

Non contrassegnare un metodo in alcun modo significa: associare questo metodo utilizzando il tipo di compilazione dell'oggetto, non il tipo di runtime (associazione statica).

Contrassegnare un metodo con virtualsignifica: associare questo metodo usando il tipo di runtime dell'oggetto, non compilare il tipo di tempo (associazione dinamica).

Contrassegnare un virtualmetodo di classe base con overridein classe derivata significa: Questo è il metodo da associare usando il tipo di runtime dell'oggetto (associazione dinamica).

Contrassegnare un virtualmetodo di classe base con newin classe derivata significa: Questo è un nuovo metodo, che non ha alcuna relazione con quello con lo stesso nome nella classe base e dovrebbe essere associato usando il tipo di tempo di compilazione dell'oggetto (associazione statica).

Non contrassegnare un virtualmetodo di classe base nella classe derivata significa: Questo metodo è contrassegnato come new(associazione statica).

Contrassegnare un metodo abstractsignifica: questo metodo è virtuale, ma non dichiarerò un corpo per esso e anche la sua classe è astratta (associazione dinamica).

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.