nouveau mot-clé dans la signature de méthode

113

Lors d'un refactoring, j'ai fini par créer une méthode comme l'exemple ci-dessous. Le type de données a été modifié par souci de simplicité.

J'avais auparavant une déclaration de mission comme celle-ci:

MyObject myVar = new MyObject();

Il a été remanié à ceci par accident:

private static new MyObject CreateSomething()
{
  return new MyObject{"Something New"};
}

C'était le résultat d'une erreur de copier / coller de ma part, mais le newmot clé in private static newest valide et compile.

Question : Que signifie le newmot - clé dans une signature de méthode? Je suppose que c'est quelque chose d'introduit dans C # 3.0?

En quoi cela diffère- overridet-il?

p.campbell
la source
5
Quelques notes sur l'opportunité de cacher une méthode: blogs.msdn.com/ericlippert/archive/2008/05/21/…
Eric Lippert
2
@Eric .. Excellent message. Je n'ai jamais pensé que la méthode se cachait de cette façon, comme oui, l'objet EST une chose, mais nous le présentons maintenant comme quelque chose d'autre, donc nous voulons le comportement de la chose que nous le présentons comme. Clever ...
BFree
1
Une question en double dans le futur et j'ai essayé de répondre en détail: Utilisez un nouveau mot-clé en c #
Ken Kin
1
L'utilisation de newcomme modificateur sur une méthode (ou un autre membre de type) n'est pas "quelque chose d'introduit dans C # 3.0". Il existe depuis la première version de C #.
Jeppe Stig Nielsen
1
Il y a comme 4 doublons de cette question dans SO. À mon avis, celui-ci vous donnera la meilleure compréhension: stackoverflow.com/questions/3117838/… . Il est répondu par @EricLippert.
Raikol Amaro

Réponses:

101

Nouvelle référence de mot clé de MSDN:

Référence MSDN

Voici un exemple que j'ai trouvé sur le net d'un MVP Microsoft qui avait du bon sens: Lien vers l'original

public class A
{
   public virtual void One();
   public void Two();
}

public class B : A
{
   public override void One();
   public new void Two();
}

B b = new B();
A a = b as A;

a.One(); // Calls implementation in B
a.Two(); // Calls implementation in A
b.One(); // Calls implementation in B
b.Two(); // Calls implementation in B

Le remplacement ne peut être utilisé que dans des cas très spécifiques. Depuis MSDN:

Vous ne pouvez pas remplacer une méthode non virtuelle ou statique. La méthode de base remplacée doit être virtuelle, abstraite ou remplacée.

Le mot-clé «nouveau» est donc nécessaire pour vous permettre de «remplacer» les méthodes non virtuelles et statiques.

Kelsey
la source
4
je viens d'exécuter votre échantillon .. il remplacera même si vous ne spécifiez pas "nouveau", non?
Michael Sync
2
@MichaelSync exactement, alors pourquoi avons-nous besoin de mentionner un nouveau mot-clé?
ZoomIn
2
"Bien que vous puissiez masquer les membres sans utiliser le nouveau modificateur, vous obtenez un avertissement du compilateur." par le document lié. Ainsi, le mot-clé indique clairement que vous aviez l'intention de vous cacher et l'avertissement n'est pas émis.
Jim compte
1
-1 -> ce n'est pas du tout obligatoire - le comportement sera le même. Ce qui est très déroutant pour un débutant new, mais je pense que ce n'est littéralement là que pour la lisibilité - le compilateur vous avertit simplement que lorsque vous appelez une méthode de classe dérivée du même nom que base, vous n'obtiendrez pas la méthode de la classe de base que vous pouvez pensez ... c'est bizarre ... uniquement pour la lisibilité?
Don Cheadle
1
Par souci d'exhaustivité: si les classes A et B implémentent toutes deux une interface, IMyInterfacel'implémentation sur la classe Derived sera appelée. Ainsi IMyInterface c = new B()appellera l'implémentation de la classe B. Si une seule classe implémente l'interface, la méthode de la classe qui l'implémente sera appelée.
Nullius
61

Non, ce n'est en fait pas «nouveau» (pardonnez le jeu de mots). Il est essentiellement utilisé pour «cacher» une méthode. C'EST À DIRE:

public class Base
{
   public virtual void Method(){}
}

public class Derived : Base
{
   public new void Method(){}
}

Si vous faites ensuite ceci:

Base b = new Derived();
b.Method();

La méthode de la base est celle qui sera appelée, PAS celle de la méthode dérivée.

Quelques informations supplémentaires: http://www.akadia.com/services/dotnet_polymorphism.html

Re votre modification: Dans l'exemple que j'ai donné, si vous deviez "remplacer" au lieu d'utiliser "new" alors quand vous appelez b.Method (); la méthode de la classe dérivée serait appelée à cause du polymorphisme.

BGratuit
la source
public class Base {public virtual void Method () {Console.WriteLine ("Base"); }} classe publique Derived: Base {Public void Method () {Console.WriteLine ("Derived"); }} Base b = new Derived (); b.Méthode (); J'ai "Base" .. Si j'ajoute "new" dans la classe Derived, j'obtiens toujours "Base" ..
Michael Sync
@michael la méthode est toujours virtuelle
Rune FS
@MichaelSync: Vous devriez voir un avertissement avec ce code; Avertissement Derived.Method () 'masque le membre hérité' Base.Method () '. Pour que le membre actuel remplace cette implémentation, ajoutez le mot-clé override. Sinon, ajoutez le nouveau mot-clé.
Christopher McAtackney
2
@MichaelSync Si le mot "override" est omis, alors le comportement par défaut est "new", par exemple le masquage de méthode. Donc, le fait de laisser le mot "nouveau" ne fait aucune différence.
BFree
1
Oui. C'est ce que je pense aussi. Je ne sais pas pourquoi C # a ajouté "nouveau" mot-clé .. c'est juste pour faire disparaître l'avertissement .. stackoverflow.com/questions/8502661/…
Michael Sync
22

Comme d'autres l'ont expliqué, il est utilisé pour masquer une méthode existante. C'est utile pour remplacer une méthode qui n'est pas virtuelle dans la classe parent.

Gardez à l'esprit que la création d'un "nouveau" membre n'est pas polymorphe. Si vous transtypez l'objet vers le type de base, il n'utilisera pas le membre du type dérivé.

Si vous avez une classe de base:

public class BaseClass
{
    public void DoSomething() { }
}

Et puis la classe dérivée:

public class DerivedType : BaseClass
{
    public new void DoSomething() {}

}

Si vous déclarez un type de DerivedTypeet que vous le castez, la méthode DoSomething()n'est pas polymorphe, elle appellera la méthode de la classe de base, pas celle dérivée.

BaseClass t = new DerivedType();
t.DoSomething();// Calls the "DoSomething()" method of the base class.
Dan Herbert
la source
1
Il appellera la méthode de la classe de base même si vous supprimez également "new" de DerievedType.
Michael Sync
2
Je pense que votre deuxième paragraphe cloue tout ce sujet. Lorsqu'il s'agit d'appels à partir d'une référence de classe de base , "new" n'est pas polymorphe ... ie. Vous obtenez exactement ce que vous spécifiez lorsque vous passez l'appel. "override" est polymorphe ... ie. Vous obtenez ce que la hiérarchie de classes spécifie.
Jonathon Reinhart
comment est-ce quelque chose de si vague défini? Très frustrant pour un débutant. Et non, cela n'a rien à voir avec le polymorphisme - il a exactement le même comportement que de simplement ne pas avoir overridede signature de méthode
Don Cheadle
Qu'est-ce qui est caché de quoi? Cela ne peut certainement pas newmasquer le type de base du type dérivé, car ne pouvez-vous pas toujours accéder au type de base en utilisant la base.<type>notation?
thatWiseGuy
@thatWiseGuy C'est "caché" dans le sens où la méthode de base ne sera pas appelée lors de l'utilisation de la classe dérivée dans votre code. Il peut toujours être appelé en interne à l'aide de base.<method>, et peut également être appelé en externe si vous convertissez le type de base dans votre code. Il est techniquement plus précis de le considérer comme «écrasant» la méthode de base que comme «masquant».
Dan Herbert
7

À partir de la documentation:

Si la méthode de la classe dérivée est précédée du mot-clé new, la méthode est définie comme étant indépendante de la méthode de la classe de base.

Ce que cela signifie en pratique:

Si vous héritez d'une autre classe et que vous avez une méthode qui partage la même signature, vous pouvez la définir comme «nouvelle» afin qu'elle soit indépendante de la classe parente. Cela signifie que si vous avez une référence à la classe «parent», cette implémentation sera exécutée, si vous avez une référence à la classe enfant, cette implémentation sera exécutée.

Personnellement, j'essaie d'éviter le mot-clé «nouveau» car cela signifie normalement que ma hiérarchie de classes est erronée, mais il y a des moments où cela peut être utile. Un endroit est pour la gestion des versions et la compatibilité ascendante.

Il y a beaucoup d'informations dans le MSDN pour cela .

jonnii
la source
Le fait que ce problème se produise n'a rien à voir avec la newprésence. C'est syntaxique / lisibilité.
Don Cheadle
3

Cela signifie que la méthode remplace une méthode par le même nom hérité par la classe de base. Dans votre cas, vous n'avez probablement pas de méthode portant ce nom dans la classe de base, ce qui signifie que le nouveau mot-clé est totalement superflu.

Robin Clowers
la source
3

Pour faire court - ce n'est PAS nécessaire, cela ne change AUCUN comportement et il est PUREMENT là pour la lisibilité.

C'est pourquoi dans VS, vous verrez un peu ondulé, mais votre code se compilera et fonctionnera parfaitement bien et comme prévu.

Il faut se demander si cela valait vraiment la peine de créer le newmot - clé alors que tout cela signifie que le développeur reconnaît: "Oui, je sais que je cache une méthode de base, oui je sais que je ne fais rien en rapport avec virtualou overriden(polymorphisme) - Je veux vraiment créer sa propre méthode ».

C'est un peu bizarre pour moi, mais peut-être seulement parce que je viens d'un Javaarrière-plan et qu'il y a cette différence fondamentale entre l' C#héritage et Java: Dans Java, les méthodes sont virtuelles par défaut, sauf si spécifié par final. Dans C#, les méthodes sont finales / concrètes par défaut, sauf si spécifié par virtual.

Don Cheadle
la source
1

Depuis MSDN :

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.

Doug R
la source
0

Faites attention à ce piège.
Vous avez une méthode définie dans une interface qui est implémentée dans une classe de base. Vous créez ensuite une classe dérivée qui masque la méthode de l'interface, mais ne déclarez pas spécifiquement la classe dérivée comme implémentant l'interface. Si vous appelez ensuite la méthode via une référence à l'interface, la méthode de la classe de base sera appelée. Cependant, si votre classe dérivée implémente spécifiquement l'interface, sa méthode sera appelée quel que soit le type de référence utilisé.

interface IMethodToHide
{
    string MethodToHide();
}

class BaseWithMethodToHide : IMethodToHide
{
    public string MethodToHide()
    {
        return "BaseWithMethodToHide";
    }
}

class DerivedNotImplementingInterface   : BaseWithMethodToHide
{
    new public string MethodToHide()
    {
        return "DerivedNotImplementingInterface";
    }
}

class DerivedImplementingInterface : BaseWithMethodToHide, IMethodToHide
{
    new public string MethodToHide()
    {
        return "DerivedImplementingInterface";
    }
}

class Program
{
    static void Main()
    {
        var oNoI = new DerivedNotImplementingInterface();
        IMethodToHide ioNoI = new DerivedNotImplementingInterface();

        Console.WriteLine("reference to the object type DerivedNotImplementingInterface calls the method in the class " 
            + oNoI.MethodToHide());
        // calls DerivedNotImplementingInterface.MethodToHide()
        Console.WriteLine("reference to a DerivedNotImplementingInterface object via the interfce IMethodToHide calls the method in the class " 
            + ioNoI.MethodToHide());
        // calls BaseWithMethodToHide.MethodToHide()
        Console.ReadLine();

        var oI = new DerivedImplementingInterface();
        IMethodToHide ioI = new DerivedImplementingInterface();

        Console.WriteLine("reference to the object type DerivedImplementingInterface calls the method in the class " 
            + oI.MethodToHide());
        // calls DerivedImplementingInterface.MethodToHide()
        Console.WriteLine("reference to a DerivedImplementingInterface object via the interfce IMethodToHide calls the method in the class " 
            + ioI.MethodToHide());
        // calls DerivedImplementingInterface.MethodToHide()
        Console.ReadLine();

    }
}
Manteau trop rempli
la source