Pourquoi est-il nécessaire d'avoir un mot-clé de remplacement devant les méthodes abstraites lorsque nous les implémentons dans une classe enfant?


9

Lorsque nous créons une classe qui hérite d'une classe abstraite et lorsque nous implémentons la classe abstraite héritée, pourquoi devons-nous utiliser le mot-clé override?

public abstract class Person
{
    public Person()
    {

    }

    protected virtual void Greet()
    {
        // code
    }

    protected abstract void SayHello();
}

public class Employee : Person
{
    protected override void SayHello() // Why is override keyword necessary here?
    {
        throw new NotImplementedException();
    }

    protected override void Greet()
    {
        base.Greet();
    }
}

Puisque la méthode est déclarée abstraite dans sa classe parente, elle n'a pas d'implémentation dans la classe parente, alors pourquoi le remplacement de mot clé est-il nécessaire ici?


2
Une méthode abstraite est implicitement une méthode virtuelle, selon les spécifications
Pavel Anikhouski


"Le modificateur de remplacement est nécessaire pour étendre ou modifier l'implémentation abstraite ou virtuelle d'une méthode, d'une propriété, d'un indexeur ou d'un événement hérité." docs.microsoft.com/en-us/dotnet/csharp/language-reference/…
gunr2171

2
Parce que si vous ne le mettez pas là, vous "cachez" la méthode au lieu de la remplacer. C'est pourquoi vous obtenez l'avertissement que "si c'est ce que vous vouliez, utilisez le newmot clé` ... Vous obtiendrez également l'erreur" aucune méthode ne remplace la méthode de classe de base ".
Ron Beyer

@RonBeyer avec une méthode virtuelle oui, mais avec un résumé, il ne compilerait tout simplement pas.
Johnathan Barclay

Réponses:


14

Lorsque nous créons une classe qui hérite d'une classe abstraite et lorsque nous implémentons la classe abstraite héritée, pourquoi devons-nous utiliser le mot-clé override?

"Pourquoi?" des questions comme celle-ci peuvent être difficiles à répondre car elles sont vagues. Je vais supposer que votre question est "quels arguments pourraient être avancés lors de la conception du langage pour plaider en faveur de la nécessité du overridemot-clé ?"

Commençons par prendre du recul. Dans certains langages, par exemple Java, les méthodes sont virtuelles par défaut et remplacées automatiquement. Les concepteurs de C # en étaient conscients et considéraient qu'il s'agissait d'une faille mineure en Java. C # n'est pas "Java avec les parties stupides retirées" comme certains l'ont dit, mais les concepteurs de C # étaient désireux d'apprendre des points de conception problématiques de C, C ++ et Java, et de ne pas les reproduire en C #.

Les concepteurs C # ont considéré que la surcharge était une source possible de bogues; après tout, c'est un moyen de changer le comportement du code existant et testé , et c'est dangereux. Le dépassement n'est pas quelque chose qui devrait être fait avec désinvolture ou par accident; il doit être conçu par quelqu'un qui y pense sérieusement . C'est pourquoi les méthodes ne sont pas virtuelles par défaut, et pourquoi vous devez dire que vous remplacez une méthode.

Voilà le raisonnement de base. Nous pouvons maintenant entrer dans un raisonnement plus avancé.

La réponse de StriplingWarrior donne une bonne première coupe pour faire un argument plus avancé. L'auteur de la classe dérivée peut ne pas être informé de la classe de base, avoir l'intention de créer une nouvelle méthode et nous ne devons pas permettre à l'utilisateur de remplacer par erreur .

Bien que ce point soit raisonnable, il existe un certain nombre de contre-arguments, tels que:

  • L'auteur d'une classe dérivée a la responsabilité de tout savoir sur la classe de base! Ils réutilisent ce code et devraient faire preuve de diligence raisonnable pour bien comprendre ce code avant de le réutiliser.
  • Dans votre scénario particulier, la méthode virtuelle est abstraite; ce serait une erreur de ne pas le remplacer, et il est donc peu probable que l'auteur crée une implémentation par accident.

Faisons alors un argument encore plus avancé sur ce point. Dans quelles circonstances l'auteur d'une classe dérivée peut-il être excusé de ne pas savoir ce que fait la classe de base? Eh bien, considérez ce scénario:

  • L'auteur de la classe de base crée une classe de base abstraite B.
  • L'auteur d'une classe dérivée, dans une autre équipe, crée une classe dérivée D avec la méthode M.
  • L'auteur de la classe de base se rend compte que les équipes qui étendent la classe de base B devront toujours fournir une méthode M, donc l'auteur de la classe de base ajoute la méthode abstraite M.
  • Lorsque la classe D est recompilée, que se passe-t-il?

Ce que nous voulons, c'est que l'auteur de D soit informé que quelque chose de pertinent a changé . Ce qui a changé, c'est que M est désormais une exigence et que leur mise en œuvre doit être surchargée. DM peut avoir besoin de changer son comportement une fois que nous savons qu'il peut être appelé à partir de la classe de base. La bonne chose à faire n'est pas de dire silencieusement "oh, le DM existe et étend le BM". La bonne chose à faire pour le compilateur est d' échouer , et de dire "hé, auteur de D, vérifiez cette hypothèse qui n'est plus valide et corrigez votre code si nécessaire".

Dans votre exemple, supposez que l' optionoverride était facultative , SayHellocar elle remplace une méthode abstraite. Il y a deux possibilités: (1) l'auteur du code a l'intention de remplacer une méthode abstraite, ou (2) la méthode de substitution est prioritaire par accident parce que quelqu'un d'autre a changé la classe de base, et le code est maintenant erroné d'une manière subtile. Nous ne pouvons pas distinguer ces possibilités si elles overridesont facultatives .

Mais si cela overrideest nécessaire, nous pouvons distinguer trois scénarios. S'il y a une erreur possible dans le code , puis overrideest manquant . S'il est intentionnellement prioritaire, il overrideest alors présent . Et s'il n'est pas intentionnellement prioritaire, alors il newest présent . Le design de C # nous permet de faire ces distinctions subtiles.

N'oubliez pas que le rapport d'erreurs du compilateur nécessite la lecture de l'esprit du développeur ; le compilateur doit déduire du mauvais code le code correct que l'auteur avait probablement en tête et donner une erreur qui les pointe dans la bonne direction. Plus nous pouvons faire en sorte que le développeur laisse dans le code ce qu'il pensait, mieux le compilateur peut faire de rapport d'erreurs et donc plus vite vous pouvez trouver et corriger vos bogues.

Mais plus généralement, C # a été conçu pour un monde dans lequel le code change . Un grand nombre de fonctionnalités de C # qui semblent "bizarres" sont en fait là car elles informent le développeur lorsqu'une supposition qui était valide est devenue invalide car une classe de base a changé. Cette classe de bogues est appelée "échecs de classe de base fragiles", et C # a un certain nombre d'atténuations intéressantes pour cette classe d'échec.


4
Merci d'avoir élaboré. J'apprécie toujours vos réponses, à la fois parce que c'est bien d'avoir une voix d'autorité de quelqu'un "à l'intérieur" et parce que vous faites un excellent travail pour expliquer les choses simplement et complètement.
StriplingWarrior

Merci pour l'explication détaillée! j'apprécie beaucoup.
psj01

Grands points! Pourriez-vous énumérer certaines des autres atténuations que vous mentionnez?
aksh1618

3

Il s'agit de spécifier si vous essayez de remplacer une autre méthode dans la classe parente ou de créer une nouvelle implémentation unique à ce niveau de la hiérarchie des classes. Il est concevable qu'un programmeur ne soit pas au courant de l'existence d'une méthode dans une classe parent avec exactement la même signature que celle qu'il crée dans sa classe, ce qui pourrait conduire à de mauvaises surprises.

S'il est vrai qu'une méthode abstraite doit être remplacée dans une classe enfant non abstraite, les créateurs de C # ont probablement estimé qu'il valait toujours mieux être explicite sur ce que vous essayez de faire.


C'est un bon début pour comprendre le raisonnement de l'équipe de conception linguistique; J'ai ajouté une réponse qui montre comment l'équipe commence avec l'idée que vous avez exprimée ici, mais va plus loin.
Eric Lippert

1

Parce que la abstractméthode est une méthode virtuelle sans implémentation, selon la spécification du langage C # , signifie que la méthode abstraite est implicitement une méthode virtuelle. Et overrideest utilisé pour étendre ou modifier l'implémentation abstraite ou virtuelle, comme vous pouvez le voir ici

Pour le reformuler un peu - vous utilisez des méthodes virtuelles pour implémenter une sorte de liaison tardive, tandis que les méthodes abstraites forcent les sous-classes du type à avoir la méthode explicitement remplacée. C'est le point, quand la méthode est virtual, elle peut être remplacée, quand c'est une abstract- elle doit être remplacée


1
Je pense que la question n'est pas de savoir ce qu'elle fait, mais pourquoi elle doit être explicite.
Johnathan Barclay

0

Pour ajouter à la réponse de @ StriplingWarrior, je pense que cela a également été fait pour avoir une syntaxe qui est cohérente avec le remplacement d'une méthode virtuelle dans la classe de base.

public abstract class MyBase
{
    public virtual void MyVirtualMethod() { }

    public virtual void MyOtherVirtualMethod() { }

    public abstract void MyAbtractMethod();
}

public class MyDerived : MyBase
{
    // When overriding a virtual method in MyBase, we use the override keyword.
    public override void MyVirtualMethod() { }

    // If we want to hide the virtual method in MyBase, we use the new keyword.
    public new void MyOtherVirtualMethod() { }

    // Because MyAbtractMethod is abstract in MyBase, we have to override it: 
    // we can't hide it with new.
    // For consistency with overriding a virtual method, we also use the override keyword.
    public override void MyAbtractMethod() { }
}

Donc, C # aurait pu être conçu de manière à ce que vous n'ayez pas besoin du mot-clé override pour remplacer les méthodes abstraites, mais je pense que les concepteurs ont décidé que ce serait déroutant car il ne serait pas cohérent avec le remplacement d'une méthode virtuelle.


Re: "les concepteurs ont décidé que ce serait déroutant car cela ne serait pas compatible avec le remplacement d'une méthode virtuelle" - oui, mais plus. Supposons que vous ayez une classe de base B avec une méthode virtuelle M et une classe dérivée D avec un remplacement. Supposons maintenant que l'auteur de B décide de faire M abstrait. C'est un changement de rupture, mais peut-être qu'ils le font. Question: l'auteur de D devrait-il être obligé de retirer le override? Je pense que la plupart des gens conviendraient qu'il est absurde de forcer l'auteur de D à effectuer un changement de code inutile; leur classe va bien!
Eric Lippert
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.