web-dev-qa-db-fra.com

Pourquoi avons-nous besoin du nouveau mot-clé et pourquoi le comportement par défaut est-il à masquer et non à remplacer?

Je regardais cela article de blog et j'avais les questions suivantes:

  • Pourquoi avons-nous besoin du mot clé new, est-ce juste pour spécifier qu'une méthode de classe de base est masquée. Je veux dire, pourquoi en avons-nous besoin? Si nous n'utilisons pas le mot clé override, ne masquons-nous pas la méthode de la classe de base?
  • Pourquoi la valeur par défaut en C # est-elle à masquer et à ne pas remplacer? Pourquoi les concepteurs l'ont-ils mis en œuvre de cette façon?
80
Sandbox

Bonnes questions. Permettez-moi de les reformuler.

Pourquoi est-il légal de cacher une méthode avec une autre méthode?

Permettez-moi de répondre à cette question avec un exemple. Vous avez une interface de CLR v1:

interface IEnumerable
{
    IEnumerator GetEnumerator();
}

Super. Maintenant, dans CLR v2, vous avez des génériques et vous pensez "mec, si seulement nous avions eu des génériques dans v1 j'aurais fait de cela une interface générique. Mais je ne l'ai pas fait. Je devrais faire quelque chose de compatible avec ça maintenant que est générique afin que j'obtienne les avantages des génériques sans perdre la rétrocompatibilité avec le code qui attend IEnumerable. "

interface IEnumerable<T> : IEnumerable
{
    IEnumerator<T> .... uh oh

Comment allez-vous appeler la méthode GetEnumerator de IEnumerable<T>? N'oubliez pas que vous voulez qu'il masque GetEnumerator sur l'interface de base non générique. Vous ne voulez jamais que cette chose soit appelée à moins que vous ne soyez explicitement dans une situation de compatibilité ascendante.

Cela justifie à lui seul la méthode cachée. Pour plus de réflexions sur les justifications de la méthode cachée, voir mon article sur le sujet .

Pourquoi se cacher sans "nouveau" provoque-t-il un avertissement?

Parce que nous voulons vous signaler que vous cachez quelque chose et que vous le faites accidentellement. Rappelez-vous, vous pourriez cacher quelque chose accidentellement à cause d'une modification de la classe de base effectuée par quelqu'un d'autre, plutôt que par la modification de votre classe dérivée.

Pourquoi se cacher sans "nouveau" un avertissement plutôt qu'une erreur?

Même raison. Vous pourriez cacher quelque chose accidentellement parce que vous venez de prendre une nouvelle version d'une classe de base. Ça arrive tout le temps. FooCorp crée une classe de base B. BarCorp crée une classe dérivée D avec une méthode Bar, car leurs clients aiment cette méthode. FooCorp voit cela et dit bon, c'est une bonne idée, nous pouvons mettre cette fonctionnalité sur la classe de base. Ils le font et expédient une nouvelle version de Foo.DLL, et lorsque BarCorp reprend la nouvelle version, ce serait bien qu'on leur dise que leur méthode masque désormais la méthode de la classe de base.

Nous voulons que cette situation soit un avertissement et non une erreur parce que faire une erreur signifie que c'est une autre forme du problème de la classe de base fragile . C # a été soigneusement conçu pour que lorsque quelqu'un apporte une modification à une classe de base, les effets sur le code qui utilise une classe dérivée soient minimisés.

Pourquoi masquer et ne pas remplacer la valeur par défaut?

Parce que le remplacement virtuel est dangereux . La substitution virtuelle permet aux classes dérivées de modifier le comportement du code qui a été compilé pour utiliser les classes de base. Faire quelque chose de dangereux comme faire un remplacement devrait être quelque chose que vous faites consciemment et délibérément , pas par accident.

125
Eric Lippert

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

Cependant, si vous ne spécifiez ni new ni overrides, la sortie résultante est la même que si vous avez spécifié new, mais vous obtiendrez un avertissement du compilateur (car vous ne savez peut-être pas que vous cachez une méthode dans la méthode de classe de base, ou bien vous avez peut-être voulu le remplacer et avez simplement oublié d'inclure le mot-clé).

Ainsi, cela vous aide à éviter les erreurs et à montrer explicitement ce que vous voulez faire et cela rend le code plus lisible, de sorte que l'on peut facilement comprendre votre code.

14
Incognito

Il convient de noter que l'effet uniquement de new dans ce contexte est de supprimer un avertissement. Il n'y a aucun changement de sémantique.

Donc, une réponse est: Nous avons besoin de new pour signaler au compilateur que la dissimulation est intentionnelle et pour se débarrasser de l'avertissement.

La question de suivi est la suivante: si vous ne voulez pas/ne pouvez pas remplacer une méthode, pourquoi introduiriez-vous une autre méthode du même nom? Parce que se cacher est par essence un conflit de nom. Et vous l'éviterez bien sûr dans la plupart des cas.

La seule bonne raison à laquelle je peux penser pour me cacher intentionnellement est quand un nom vous est imposé par une interface.

12
Henk Holterman

En C #, les membres sont scellés par défaut, ce qui signifie que vous ne pouvez pas les remplacer (sauf s'ils sont marqués par les mots clés virtual ou abstract) et ceci pour des raisons de performances . Le nouveau modificateur est utilisé pour masquer explicitement un membre hérité.

5
Darin Dimitrov

Si le remplacement était par défaut sans spécifier le mot clé override, vous pourriez accidentellement remplacer une méthode de votre base juste en raison de l'égalité de nom.

La stratégie du compilateur .Net consiste à émettre des avertissements si quelque chose ne va pas, juste pour être sûr, donc dans ce cas, si la substitution était par défaut, il devrait y avoir un avertissement pour chaque méthode remplacée - quelque chose comme 'avertissement: vérifiez si vous voulez vraiment remplacer '.

2

Ma supposition serait principalement due à l'héritage d'interfaces multiples. En utilisant des interfaces discrètes, il serait très possible que deux interfaces distinctes utilisent la même signature de méthode. Autoriser l'utilisation du mot clé new vous permettrait de créer ces différentes implémentations avec une seule classe, au lieu d'avoir à créer deux classes distinctes.

Mise à jour ... Eric m'a donné une idée sur la façon d'améliorer cet exemple.

public interface IAction1
{
    int DoWork();
}
public interface IAction2
{
    string DoWork();
}

public class MyBase : IAction1
{
    public int DoWork() { return 0; }
}
public class MyClass : MyBase, IAction2
{
    public new string DoWork() { return "Hi"; }
}

class Program
{
    static void Main(string[] args)
    {
        var myClass = new MyClass();
        var ret0 = myClass.DoWork(); //Hi
        var ret1 = ((IAction1)myClass).DoWork(); //0
        var ret2 = ((IAction2)myClass).DoWork(); //Hi
        var ret3 = ((MyBase)myClass).DoWork(); //0
        var ret4 = ((MyClass)myClass).DoWork(); //Hi
    }
}
0
Matthew Whited

Comme indiqué, le masquage de méthode/propriété permet de changer des choses sur une méthode ou une propriété qui ne pourraient pas être facilement modifiées autrement. Une situation où cela peut être utile consiste à autoriser une classe héritée à avoir des propriétés en lecture-écriture qui sont en lecture seule dans la classe de base. Par exemple, supposons qu'une classe de base possède un tas de propriétés en lecture seule appelées Value1-Value40 (bien sûr, une vraie classe utiliserait de meilleurs noms). Un descendant scellé de cette classe a un constructeur qui prend un objet de la classe de base et copie les valeurs à partir de là; la classe ne permet pas de les changer après cela. Un descendant différent, héritable, déclare une propriété en lecture-écriture appelée Value1-Value40 qui, lorsqu'elle est lue, se comporte de la même manière que les versions de la classe de base mais, lorsqu'elle est écrite, permet d'écrire les valeurs. L'effet net sera ce code qui veut qu'une instance de la classe de base qu'il sait ne changera jamais peut créer un nouvel objet de la classe en lecture seule, qui peut copier des données à partir d'un objet transmis sans avoir à se soucier si cet objet est en lecture seule ou en lecture-écriture.

Un inconvénient de cette approche - peut-être que quelqu'un peut m'aider - est que je ne connais pas de moyen d'ombrer et de remplacer une propriété particulière au sein de la même classe. Est-ce que l'un des langages CLR le permet (j'utilise vb 2005)? Il serait utile que l'objet de classe de base et ses propriétés soient abstraits, mais cela nécessiterait une classe intermédiaire pour remplacer les propriétés Value1 à Value40 avant qu'une classe descendante puisse les masquer.

0
supercat