2009-09-24 14 views
5

Essendo principalmente uno sviluppatore Java, sono rimasto un po 'sorpreso dal risultato quando un giorno ho utilizzato per errore la nuova parola chiave anziché override.Qual è il caso d'uso per C# che consente di usare nuovo su un metodo virtuale?

Sembra che il nuovo parola chiave rimuove la "virtualità" del metodo a quel livello nella struttura di eredità, in modo che chiamando un metodo su un'istanza della classe figlia che è downcasted alla classe genitore, non risolverà all'implementazione del metodo nella classe figlio.

Quali sono i casi di utilizzo pratico di questo comportamento?

Precisazione: comprendo l'uso del nuovo quando genitore non è virtuale. Sono più curioso del perché il compilatore consenta di combinare nuovi e virtuali.

L'esempio seguente illustra la differenza:

using System; 

public class FooBar 
{ 
    public virtual void AAA() 
    { 
     Console.WriteLine("FooBar:AAA"); 
    } 

    public virtual void CCC() 
    { 
     Console.WriteLine("FooBar:CCC"); 
    } 
} 

public class Bar : FooBar 
{ 
    public new void AAA() 
    { 
     Console.WriteLine("Bar:AAA"); 
    } 

    public override void CCC() 
    { 
     Console.WriteLine("Bar:CCC"); 
    } 
} 

public class TestClass 
{ 
    public static void Main() 
    { 
     FooBar a = new Bar(); 
     Bar b = new Bar(); 
     Console.WriteLine("Calling FooBar:AAA"); 
     a.AAA(); 
     Console.WriteLine("Calling FooBar:CCC"); 
     a.CCC(); 
     Console.WriteLine("Calling Bar:AAA"); 
     b.AAA(); 
     Console.WriteLine("Calling Bar:CCC"); 
     b.CCC(); 
     Console.ReadLine(); 
    } 
} 

Questo produce il seguente output:

Calling FooBar:AAA 
FooBar:AAA 
Calling FooBar:CCC 
Bar:CCC 
Calling Bar:AAA 
Bar:AAA 
Calling Bar:CCC 
Bar:CCC 

risposta

15

Caso d'uso:

  • Oggi, si utilizza una libreria di 3a parte e trarre classe Banana dalla classe Fruit.
  • Si implementa un metodo chiamato Peel in Banana. Non c'è Peel in Fruit.
  • Domani, la terza parte rilascia una nuova versione della libreria, incluso un metodo virtuale Fruit.Peel
  • Ricompilate il codice domani. Vuoi eseguire l'override di Fruit.Peel? Molto probabilmente no - potrebbe avere un significato completamente diverso. Invece, è nascondi con Banana.Peel e tutto il codice esistente funziona come fa oggi.

In altre parole, è principalmente per evitare problemi di versioning. In Java, finiresti per ignorare lo Fruit.peel anche se non lo volevi, il che probabilmente potrebbe portare a bachi difficili da diagnosticare.

+0

Buon esempio. È un'opzione molto conveniente, ma direi che ha abbastanza insidie ​​da giustificare un avviso di compilazione. – PeterR

+0

Bene si ottiene un avviso di compilazione se * non * si ha 'new' ma si nasconde un metodo. L'idea è che se * esplicitamente * aggiungi un modificatore, questo dovrebbe essere un segnale che intendi davvero :) –

2

Parlando per esperienza personale, io per lo più vedere la "nuova" parola chiave utilizzata nei casi in cui l'originale il metodo principale era non specificato come virtuale, ma si desiderava un comportamento di override. L'applicazione della "nuova" parola chiave "nasconde" il metodo genitore. E, come hai osservato nell'esempio di codice, il metodo scritto con "nuovo" verrà eseguito solo quando si lavora direttamente con quel tipo. Se si lavora con il tipo genitore, verrà chiamato il metodo originale dei genitori.

Per rispondere alla domanda in modo più diretto, fornisce un mezzo per sovrascrivere i metodi quando il metodo padre non è stato etichettato come virtuale.

EDIT: Per inciso, aggiungendo la "nuova" parola chiave per nascondere un metodo genitore che non è naturalmente ignorabile in realtà non cambia nulla nella IL generato. Ma è un mezzo di affermando esplicitamente per lo sviluppatore "Hey, ti stai nascondendo un metodo genitore qui, non ignorando che"

0

Ipoteticamente ....

 

public class BaseCollection<T> 
{ 
    // void return - doesn't seem to care about notifying the 
    // client where the item was added; it has an IndexOf method 
    // the caller can use if wants that information 
    public virtual void Add(T item) 
    { 
    // adds the item somewhere, doesn't say where 
    } 

    public int IndexOf(T item) 
    { 
    // tells where the item is 
    } 
} 

public class List<T> : BaseCollection<T> 
{ 
    // here we have an Int32 return because our List is friendly 
    // and will tell the caller where the item was added 
    new public virtual int Add(T item) // <-- clearly not an override 
    { 
    base.Add(item); 
    return base.IndexOf(item); 
    } 
} 

Qui io uso il "nuovo" modificatore perché un elenco <T> riferimento nasconderà il metodo Add della BaseCollection <T>. Per impostazione predefinita, nascondere i membri da una base genera un avviso dal compilatore (un errore se la compilazione è stata impostata per non riuscire sugli avvisi). Quindi sto praticamente dicendo al compilatore ... "Sì, lo so che ho nascosto il metodo Add con il ritorno del vuoto, è la funzionalità desiderata - basta andare con esso."

Problemi correlati