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 override
mot-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 , SayHello
car 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 override
sont facultatives .
Mais si cela override
est nécessaire, nous pouvons distinguer trois scénarios. S'il y a une erreur possible dans le code , puis override
est manquant . S'il est intentionnellement prioritaire, il override
est alors présent . Et s'il n'est pas intentionnellement prioritaire, alors il new
est 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.