2009-09-24 5 views
5

Être principalement un développeur Java, j'ai été un peu surpris par le résultat lorsque j'ai utilisé accidentellement un jour nouveau mot-clé au lieu de déroger.Quel est le cas d'utilisation de C# permettant d'utiliser new sur une méthode virtuelle?

Il semble que le nouveau mot-clé supprime la « virtualité » de la méthode à ce niveau dans l'arbre d'héritage, de sorte que l'appel d'une méthode sur une instance de classe enfant qui est downcasted à la classe parente, ne résoudra pas à l'implémentation de la méthode dans la classe enfant.

Quels sont les cas d'utilisation pratique de ce comportement? Clarification: Je comprends l'utilisation de nouveau lorsque le parent n'est pas virtuel. Je suis plus curieux de savoir pourquoi le compilateur permet de combiner nouveau et virtuel.

L'exemple suivant illustre la différence:

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

Ceci produit la sortie suivante:

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

Répondre

15

utilisation:

  • Aujourd'hui, vous utilisez une bibliothèque 3ème partie et dérivez classe Banana de la classe Fruit. Vous appliquez une méthode appelée Peel dans Banana. Il n'y a pas de Peel dans Fruit.
  • Demain, la 3ème partie publie une nouvelle version de la bibliothèque, y compris une méthode virtuelle Fruit.Peel
  • Vous recompilez votre code demain. Voulez-vous remplacer Fruit.Peel? Très probablement pas - cela pourrait avoir une signification complètement différente. Au lieu de cela, vous cacher avec Banana.Peel et tout le code existant fonctionne comme il le fait aujourd'hui.

En d'autres termes, c'est surtout pour éviter les problèmes de version. En Java, vous finiriez par surcharger Fruit.peel même si vous ne le vouliez pas, ce qui pourrait conduire à des bogues difficiles à diagnostiquer.

+0

Bon exemple. C'est une option très pratique, mais je dirais qu'il a assez d'embûches pour justifier un avertissement de compilation. – PeterR

+0

Eh bien vous obtenez un avertissement de compilation si vous * n'avez pas * new' mais vous cachez une méthode. L'idée est que si vous ajoutez * explicitement * un modificateur, cela devrait être un signe que vous le pensez vraiment :) –

2

Parlant de son expérience personnelle, je vois surtout le mot-clé « nouveau » utilisé dans les cas où l'original La méthode parent était et non spécifiée comme virtuelle, mais un comportement de remplacement était souhaité. L'application du "nouveau" mot-clé "cache" la méthode parente. Et, comme vous l'avez observé dans l'exemple de code, la méthode écrite avec "new" ne sera exécutée que si vous travaillez directement avec ce type. Si vous travaillez avec le type parent, la méthode originale des parents sera appelée.

Pour répondre plus directement à votre question - il fournit un moyen de substituer des méthodes lorsque la méthode parente n'était pas étiquetée virtuelle. En outre, l'ajout du mot-clé "new" pour masquer une méthode parente qui n'est pas naturellement substituable ne change rien à l'IL générée. Mais il est un moyen de dire explicitement au développeur « Hey, vous cachez une méthode mère ici, pas la redéfinir »

0

Hypothétiquement ....

 

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

Ici, j'utiliser le modificateur « nouveau », car une liste < référence T > cachera la méthode Add de la BaseCollection <T>. Par défaut, le fait de masquer les membres d'une base génère un avertissement du compilateur (une erreur si la compilation est configurée pour échouer sur les avertissements). Donc je dis en gros au compilateur ... "Ouais, je sais que j'ai caché la méthode Add avec le retour vide, c'est la fonctionnalité désirée - allez-y."

Questions connexes