web-dev-qa-db-fra.com

Utilisez un nouveau mot clé si le masquage était prévu

J'ai l'extrait de code suivant qui génère l'avertissement "Utiliser un nouveau mot clé si le masquage était prévu" dans VS2008:

public double Foo(double param)
{
   return base.Foo(param);
}

La fonction Foo() dans la classe de base est protégée et je veux l'exposer à un test unitaire en la mettant dans la classe wrapper uniquement à des fins de test unitaire. C'est à dire. la classe wrapper ne sera utilisée pour rien d'autre. Donc, une question que j'ai est: est-ce une pratique acceptée?

Revenons à l'avertissement new. Pourquoi devrais-je changer la fonction prioritaire dans ce scénario?

54
Guy

Le new indique simplement que vous savez que vous écrasez une méthode existante. Étant donné que le code existant était protected, ce n'est pas si grave - vous pouvez ajouter le new en toute sécurité pour l'empêcher de gémir.

La différence survient lorsque votre méthode fait quelque chose de différent; toute variable qui fait référence à la classe dérivée et appelle Foo() ferait quelque chose de différent (même avec le même objet) que celle qui fait référence à la classe base et appelle Foo():

SomeDerived obj = new SomeDerived();
obj.Foo(); // runs the new code
SomeBase objBase = obj; // still the same object
objBase.Foo(); // runs the old code

Cela pourrait évidemment avoir un impact sur tout code existant qui connaît SomeDerived et appelle Foo() - c'est-à-dire qu'il exécute maintenant une méthode complètement différente.

Notez également que vous pouvez le marquer protected internal Et utiliser [InternalsVisibleTo] Pour donner accès à votre test unitaire (c'est l'utilisation la plus courante de [InternalsVisibleTo]; Puis vos tests unitaires peut y accéder directement sans la classe dérivée.

69
Marc Gravell

La clé est que vous pas écrasez la méthode. Tu le caches. Si vous le remplaçiez, vous auriez besoin du mot clé override (auquel cas, à moins qu'il ne soit virtuel, le compilateur se plaindrait parce que vous ne pouvez pas remplacer une méthode non virtuelle) .

Vous utilisez le mot clé new pour dire au compilateur et à quiconque lit le code: "Ça va, je sais que cela ne fait que masquer la méthode de base et non la remplacer - c'est ce que je voulais faire."

Franchement, je pense que c'est rarement une bonne idée de cacher les méthodes - j'utiliserais un nom de méthode différent, comme Craig l'a suggéré - mais c'est une discussion différente.

38
Jon Skeet

Vous changez la visibilité sans le nom. Appelez votre fonction TestFoo et cela fonctionnera. Oui, à mon humble avis, il est acceptable de sous-classe pour cette raison.

8
Craig Stuntz

Vous trouverez toujours des situations délicates où le mot clé new peut être utilisé pour se cacher alors qu'il peut être évité la plupart du temps.

Cependant, récemment, j'avais vraiment besoin de ce mot-clé, principalement parce que le langage manque d'autres fonctionnalités de synthax appropriées pour compléter un accesseur existant, par exemple:

Si vous envisagez une classe à l'ancienne comme:

KeyedCollection<TKey, TItem>

Vous remarquerez que l'accesseur pour accéder à l'index des articles est:

TItem this[Int32 index] { get; set; }

A les deux { get; set; } et ils sont bien sûr obligatoires en raison de l'héritage concernant ICollection<T> et Collection<T>, mais il n'y en a qu'un { get; } pour avoir accédé aux articles par le biais de leurs clés (j'ai quelques suppositions à propos de cette conception et il y a de nombreuses raisons à cela, alors veuillez noter que j'ai ramassé le KeyedCollection<TKey, TItem>) uniquement à des fins d'illustration).

Quoi qu'il en soit, il n'y a qu'un seul getter pour l'accès aux clés:

TItem this[TKey key] { get; }

Mais qu'en est-il si je veux ajouter le { set; } support, techniquement parlant ce n'est pas si stupide surtout si vous continuez à raisonner de l'ancienne définition de la propriété, c'est juste une méthode ... la seule façon est d'implémenter explicitement une autre interface factice mais quand vous voulez rendre implicite vous devez venir avec le mot clé new, je cache la définition de l'accesseur, en gardant le get; définition de base et ajoutez simplement un ensemble bourré de choses personnelles pour le faire fonctionner.

Je pense que pour ce scénario très spécifique, ce mot-clé est parfaitement applicable, notamment en ce qui concerne un contexte où il n'y a pas de { get; } partie.

  public new TItem this[TKey key]
  { 
      get { return base... }
      set { ... }
  }

C'est à peu près la seule astuce pour éviter ce genre d'avertissement car le compilateur vous suggère que vous vous cachez peut-être sans réaliser ce que vous faites.

1
Perret