Domanda

Quali sono le differenze tra la dichiarazione di un metodo in un tipo di base " virtual " e quindi sovrascriverlo in un tipo figlio usando " override " parola chiave anziché utilizzare semplicemente il " nuovo " parola chiave quando si dichiara il metodo corrispondente nel tipo figlio?

È stato utile?

Soluzione

Il " nuovo " parola chiave non sostituisce, 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 ());
    }
}

Viene stampato falso, se si utilizzava 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 devi utilizzare " nuovo " è quando il metodo non è in alcun modo correlato alla versione della classe base.

Altri suggerimenti

Trovo sempre cose del genere 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 in realtà una Bar , ma lo stiamo memorizzando in una variabile di tipo Foo (è simile al casting)

Quindi il risultato sarà il seguente, a seconda che tu abbia usato virtuale / override o new quando hai dichiarato le tue classi.

 Immagine di spiegazione virtuale / override

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

La parola chiave new crea effettivamente un membro completamente nuovo che esiste solo su quel tipo specifico.

Ad 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 di tipo Bar , troverai effettivamente 2 metodi chiamati DoSomething () che sembrano esattamente uguali. Usando new nascondi effettivamente l'implementazione nella classe base, in modo che quando le classi derivano da Bar (nel mio esempio) il metodo chiama a base.DoSomething ( ) va in Bar e non in Foo .

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

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

Chiamerà il metodo VirtualFoo () ignorato della sottoclasse.

nuovo indica al compilatore che stai 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.

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.

La differenza tra la parola chiave override e la nuova parola chiave è che il primo ha la precedenza sul metodo e il secondo si nasconde il metodo.

Controlla i link seguenti per ulteriori informazioni ...

MSDN e Altro

    La parola chiave
  • new è per nascondere. - significa che stai nascondendo il tuo metodo in fase di esecuzione. L'output sarà basato sul metodo della classe base.
  • override per 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.

La mia versione della spiegazione deriva dall'utilizzo di proprietà per aiutare a comprendere le differenze.

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

new è forse fuorviante (per me lo era). Con le proprietà è più facile da 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 foo ha 2 GetSomething , dato che in realtà ha 2 versioni della proprietà, Foo e Bar , e per sapere quale usare, c # " picks " la proprietà per il tipo corrente.

Se avessi voluto usare la versione di Bar, avresti usato l'override o invece usare Foo foo .

Bar bar ha solo 1 , in quanto desidera un comportamento completamente nuovo per GetSomething .

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 virtuale significa: associare questo metodo utilizzando il tipo di runtime dell'oggetto, non il tipo di tempo di compilazione (associazione dinamica).

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

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

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

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

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top