Quelles sont les utilisations pratiques du "nouveau" modificateur en C # en ce qui concerne le masquage?

21

Un collègue et moi examinions le comportement du newmot clé en C # tel qu'il s'applique au concept de masquage. De la documentation :

Utilisez le nouveau modificateur pour masquer explicitement un membre hérité d'une classe de base. Pour masquer un membre hérité, déclarez-le dans la classe dérivée en utilisant le même nom et modifiez-le avec le nouveau modificateur.

Nous avons lu la documentation et nous comprenons ce qu'elle fait et comment elle le fait. Ce que nous ne pouvions pas vraiment comprendre, c'est pourquoi vous devriez le faire en premier lieu. Le modificateur existe depuis 2003, et nous travaillons tous les deux avec .Net depuis plus longtemps et il n'est jamais apparu.

Quand ce comportement serait-il nécessaire dans un sens pratique (par exemple: tel qu'il est appliqué à une analyse de rentabilisation)? Est-ce une fonctionnalité qui a survécu à son utilité ou est-ce qu'elle fait tout simplement assez rare dans ce que nous faisons (en particulier, nous faisons des formulaires Web et des applications MVC et certains WinForms et WPF à petit facteur)? En essayant ce mot-clé et en jouant avec lui, nous avons trouvé certains comportements qu'il permet qui semblent un peu dangereux s'ils sont mal utilisés.

Cela semble un peu ouvert, mais nous recherchons un cas d'utilisation spécifique qui peut être appliqué à une application métier qui trouve cet outil particulier utile.

Joel Etherton
la source
9
Vous l'avez peut-être lu aussi, mais il y a un article intéressant d'Eric Lippert (développeur du compilateur C #) sur les raisons pour lesquelles la dissimulation de méthode a été ajoutée à C #: blogs.msdn.com/b/ericlippert/archive/2008/05/21/… . Cela répond en partie à votre question, mais je n'ai pas d'analyse de rentabilisation à votre disposition, alors je l'ai mise dans un commentaire.
Jalayn
3
@Jalayn: L'analyse de rentabilisation est discutée par Eric dans cet article: blogs.msdn.com/b/ericlippert/archive/2004/01/07/…
Brian

Réponses:

22

Vous pouvez l'utiliser pour imiter la covariance de type retour. Explication d'Eric Lippert . Eric fournit cet exemple de code:

abstract class Enclosure
{
    protected abstract Animal GetContents();
    public Animal Contents() { return this.GetContents(); }
}
class Aquarium : Enclosure
{
    public new Fish Contents() { ... }
    protected override Animal GetContents() { return this.Contents(); }
}

Ceci est une solution de contournement. public override Fish Contents() { ... }n'est pas légal, malgré sa sécurité.

En général, vous ne devez pas utiliser la méthode de masquage, car elle est source de confusion pour les consommateurs de votre classe (l'exemple spécifique ci-dessus ne souffre pas de ce problème). Nommez simplement votre nouvelle méthode autre chose si vous ne voulez pas remplacer une méthode existante.

Une situation probable dans le monde réel où vous auriez besoin de masquer une méthode est si le fournisseur d'une classe de base a ajouté une méthode générique que vous aviez déjà ajoutée à une classe dérivée. Un tel programme compilera (et donnera des avertissements) sans le nouveau mot-clé, mais l'ajout newdit: "Je sais que ma version de cette méthode remplace la version de la classe de base. C'est horrible et déroutant, mais nous sommes coincés avec cela." C'est encore mieux que de forcer la classe dérivée à renommer sa méthode.

Le simple fait de permettre à la méthode dérivée d'être traitée comme un remplacement entraînerait des problèmes. Ignorant tout problème lié à l'implémentation du compilateur, la nouvelle méthode est sémantiquement différente de la méthode de base, mais le polymorphisme entraînerait l'appel de la nouvelle méthode lorsqu'on lui demanderait d'appeler une méthode du même nom.

Cette situation est discutée en détail dans cet article d'Eric Lippert.

Brian
la source
1
+1 pour newavoir été un marqueur pour "c'est horrible et déroutant".
Avner Shahar-Kashtan
Je pense que l'exemple d'Eric est très utile, ne serait-ce que parce que je suis dans une situation similaire ... J'ai une classe de base implémentant une méthode non triviale qui retourne TBase, et une classe dérivée qui devrait retourner un TDerived. Dans ce cas, cela ressemble à un mal nécessaire.
Kyle Baran
4

Je pense que c'est là au cas où vous en auriez besoin pour faire quelque chose auquel les concepteurs de langage n'auraient pas pensé. C # était à bien des égards une réaction aux premières versions de java. Et une chose que java a faite a été de faire des explications très explicites aux développeurs de trous de pigeon pour éliminer les possibilités de développeurs se tirant dans les pieds. C # a adopté une approche légèrement différente et a donné aux développeurs un peu plus de pouvoir pour leur permettre de se tirer une balle dans les pieds. Un exemple est le unsafemot - clé. Ce newmot-clé en est un autre.

Maintenant, ce n'est probablement pas aussi utile que, unsafemais une fois que vous entrez dans une spécification de langue, il est difficile de sortir d'une spécification de langue.

Wyatt Barnett
la source
5
Java n'a pas de nouveauté car Java traite toutes les méthodes comme virtuelles. Selon Eric Lippert, la motivation pour soutenir de nouveaux est de résoudre le problème fragile de la classe de base. L'existence de nouveaux éléments est nécessaire pour une utilisation professionnelle réelle, et pas seulement pour une utilisation de bibliothèque de bas niveau. Java n'ayant pas de nouvelles (et n'ayant pas de méthodes non virtuelles) signifie que si une classe de base introduit une nouvelle méthode qui est déjà utilisée dans les classes dérivées, le code existant peut casser, malgré le fait que le développeur de la classe de base devrait être autorisé être inconscient du code qui le consomme.
Brian
3

Cela dit au lecteur que "j'ai délibérément caché l'implémentation de cette méthode par la classe de base", par opposition à accidentellement.

Vegio
la source
5
Cela me semble impliquer la question. L'OP sait ce qu'il fait mais veut savoir pourquoi.
Brian
0

Vous voudrez peut-être que le membre précédent soit disponible via un autre nom:

class VehicleClass
{
  public int AnyProperty
  {
    get; set;
  }

  public int AnyFunction() { return 0; }
} // VehicleClass

class IntermediateClass : VehicleClass
{
  public int PreviousAnyProperty
  {
    get { return AnyProperty; }
    set { AnyProperty = value  }
  }

  public int PreviousAnyFunction() { return AnyFunction(); }
} // IntermediateClass 

class CarClass : IntermediateClass
{
  public new int AnyProperty
  {
    get ; set ;
  }

  public new int AnyFunction() { return 5; }
} // class CarClass

class ExampleClass
{

  public static void Main()
  {
    using (CarClass MyCar = new CarClass())
    {
      int AnyInt1 = MyCar.PreviousAnyProperty;
      MyCar.PreviousAnyProperty = 7;

      int AnyInt2 = MyCar.PreviousAnyFunction();

      int AnyInt3 = MyCar.AnyProperty;
      MyCar.AnyProperty = 45;

      int AnyInt4 = MyCar.AnyFunction();
    }
  } // static void Main()

} // class CarClass

À votre santé.

umlcat
la source
Je sais comment cela fonctionne, mais ma question est vraiment la suivante: pourquoi voudriez-vous que le membre précédent soit disponible sous un autre nom? Cela rompt toutes sortes de contrats, rend certaines pièces génériques inutiles.
Joel Etherton
@Joel Etherton: Comme vous le savez peut-être déjà, les développeurs doivent parfois "choisir" le code de programmation des autres. Et nous pouvons ne pas être autorisés à modifier, peut-être à étendre les classes. Et peut nécessiter d'utiliser les deux, l'ancien membre et le nouveau.
umlcat
0

Je l'ai trouvé très utile lors de la création d'une suite de tests pour du code hérité. Cela me permet de masquer les dépendances externes, comme interroger une base de données à l'intérieur d'une méthode utilisée par d'autres méthodes. Pour pouvoir tester les autres méthodes, je peux masquer la logique d'origine qui dépend des ressources externes sans avoir besoin d'ajouter le mot-clé virtuel à ces méthodes dans les classes de test.

Spacy
la source