Contrairement à Java, pourquoi C # traite-t-il les méthodes comme des fonctions non virtuelles par défaut? Est-il plus susceptible d'être un problème de performance plutôt que d'autres résultats possibles?
Je me souviens d'avoir lu un paragraphe d'Anders Hejlsberg sur plusieurs avantages que l'architecture existante apporte. Mais qu'en est-il des effets secondaires? Est-ce vraiment un bon compromis d'avoir des méthodes non virtuelles par défaut?
Les classes doivent être conçues pour que l'héritage puisse en profiter. Avoir des méthodes virtual
par défaut signifie que chaque fonction de la classe peut être déconnectée et remplacée par une autre, ce qui n'est pas vraiment une bonne chose. Beaucoup de gens pensent même que les classes auraient dû être sealed
par défaut.
Les méthodes virtual
peuvent également avoir une légère implication sur les performances. Cependant, ce n'est probablement pas la raison principale.
Je suis surpris qu'il semble y avoir un tel consensus ici que non virtuel par défaut est la bonne façon de faire les choses. Je vais descendre de l'autre côté - je pense pragmatique - de la clôture.
La plupart des justifications m'ont été lues comme l'ancien argument "Si nous vous donnons le pouvoir, vous pourriez vous blesser". Des programmeurs?!
Il me semble que le codeur qui ne savait pas assez (ou qui n'avait pas assez de temps) pour concevoir leur bibliothèque pour l'héritage et/ou l'extensibilité est le codeur qui a produit exactement la bibliothèque que je devrais probablement réparer ou peaufiner - exactement le bibliothèque où la possibilité de passer outre serait la plus utile.
Le nombre de fois où j'ai dû écrire du code de contournement laid et désespéré (ou abandonner l'utilisation et lancer ma propre solution alternative) parce que je ne peux pas passer outre, dépasse de loin le nombre de fois où j'ai été mordu ( par exemple en Java) en redéfinissant où le concepteur n'aurait peut-être pas pensé que je pourrais.
Non virtuel par défaut rend ma vie plus difficile.
MISE À JOUR: Il a été souligné [tout à fait correctement] que je n'ai pas réellement répondu à la question. Donc - et je m'excuse d'être plutôt en retard ...
Je voulais en quelque sorte être capable d'écrire quelque chose de pur comme "C # implémente des méthodes non-virtuelles par défaut car une mauvaise décision a été prise qui valorisait les programmes plus que les programmeurs". (Je pense que cela pourrait être quelque peu justifié sur la base de certaines des autres réponses à cette question - comme les performances (optimisation prématurée, n'importe qui?), Ou la garantie du comportement des classes.)
Cependant, je me rends compte que je ne ferais qu'exprimer mon opinion et non la réponse définitive que Stack Overflow désire. Je pensais sûrement qu'au plus haut niveau, la réponse définitive (mais inutile) était:
Ils ne sont pas virtuels par défaut parce que les concepteurs de langage avaient une décision à prendre et c'est ce qu'ils ont choisi.
Maintenant, je suppose que la raison exacte pour laquelle ils ont pris cette décision ne sera jamais ... oh, attendez! La transcription d'une conversation!
Il semblerait donc que les réponses et les commentaires ici sur les dangers de remplacer les API et la nécessité de concevoir explicitement l'héritage sont sur la bonne voie mais manquent tous un aspect temporel important: Anders se préoccupait principalement de maintenir implicite une classe ou une API contrat à travers les versions. Et je pense qu'il est en fait plus soucieux de permettre à la plate-forme .Net/C # de changer sous le code plutôt que de changer le code utilisateur au-dessus de la plate-forme. (Et son point de vue "pragmatique" est exactement le contraire du mien parce qu'il regarde de l'autre côté.)
(Mais ne pouvaient-ils pas simplement choisir virtuel par défaut, puis poivré "final" dans la base de code? Peut-être que ce n'est pas tout à fait la même chose ... et Anders est clairement plus intelligent que moi, donc je vais le laisser mentir.)
Parce qu'il est trop facile d'oublier qu'une méthode peut être remplacée et non conçue pour cela. C # vous fait réfléchir avant de le rendre virtuel. Je pense que c'est une excellente décision de conception. Certaines personnes (comme Jon Skeet) ont même dit que les classes devraient être fermées par défaut.
Pour résumer ce que les autres ont dit, il y a plusieurs raisons:
1 - En C #, il y a beaucoup de choses dans la syntaxe et la sémantique qui viennent directement de C++. Le fait que les méthodes n'étaient pas virtuelles par défaut en C++ a influencé C #.
2 - Avoir chaque méthode virtuelle par défaut est un problème de performances car chaque appel de méthode doit utiliser la table virtuelle de l'objet. De plus, cela limite fortement la capacité du compilateur Just-In-Time à intégrer des méthodes et à effectuer d'autres types d'optimisation.
- Plus important encore, si les méthodes ne sont pas virtuelles par défaut, vous pouvez garantir le comportement de vos classes. Lorsqu'elles sont virtuelles par défaut, comme en Java, vous ne pouvez même pas garantir qu'une simple méthode getter fera comme prévu car elle pourrait être remplacée pour faire quoi que ce soit dans une classe dérivée (bien sûr, vous pouvez, et devez, faire méthode et/ou la classe finale).
On pourrait se demander, comme l'a mentionné Zifre, pourquoi le langage C # n'est pas allé plus loin et n'a pas rendu les classes scellées par défaut. Cela fait partie de tout le débat sur les problèmes d'héritage d'implémentation, qui est un sujet très intéressant.
C # est influencé par C++ (et plus). C++ n'active pas la répartition dynamique (fonctions virtuelles) par défaut. Un (bon?) Argument pour cela est la question: "A quelle fréquence implémentez-vous des classes qui sont membres d'une recherche de classe?". Une autre raison pour éviter d'activer la répartition dynamique par défaut est l'empreinte mémoire. Une classe sans pointeur virtuel (vpointer) pointant vers un table virtuelle , est bien sûr plus petite que la classe correspondante avec la liaison tardive activée.
Il n'est pas si facile de dire "oui" ou "non" au problème de performances. La raison en est la compilation Just In Time (JIT) qui est une optimisation de l'exécution en C #.
Une autre question similaire sur " vitesse des appels virtuels .. "
La raison simple est le coût de conception et de maintenance en plus des coûts de performance. Une méthode virtuelle a un coût supplémentaire par rapport à une méthode non virtuelle car le concepteur de la classe doit planifier ce qui se passe lorsque la méthode est remplacée par une autre classe. Cela a un impact important si vous vous attendez à ce qu'une méthode particulière mette à jour l'état interne ou ait un comportement particulier. Vous devez maintenant planifier ce qui se passe lorsqu'une classe dérivée modifie ce comportement. Il est beaucoup plus difficile d'écrire du code fiable dans cette situation.
Avec une méthode non virtuelle, vous avez un contrôle total. Tout ce qui ne va pas est la faute de l'auteur original. Le code est beaucoup plus facile à raisonner.
Si toutes les méthodes C # étaient virtuelles, le vtbl serait beaucoup plus gros.
Les objets C # n'ont des méthodes virtuelles que si la classe a des méthodes virtuelles définies. Il est vrai que tous les objets ont des informations de type qui incluent un équivalent vtbl, mais si aucune méthode virtuelle n'est définie, alors seules les méthodes Object de base seront présentes.
@ Tom Hawtin: Il est probablement plus exact de dire que C++, C # et Java sont tous de la famille des langages C :)
Venant d'un arrière-plan Perl, je pense que C # a scellé le Doom de chaque développeur qui aurait pu étendre et modifier le comportement d'une classe de base à travers une méthode non virtuelle sans forcer tous les utilisateurs de la nouvelle classe à être conscients de potentiellement derrière la scène détails.
Considérez la méthode Add de la classe List. Que se passe-t-il si un développeur souhaite mettre à jour l'une des bases de données potentielles chaque fois qu'une liste particulière est ajoutée à? Si 'Add' avait été virtuel par défaut, le développeur pourrait développer une classe 'BackedList' qui a remplacé la méthode 'Add' sans forcer tout le code client à savoir qu'il s'agissait d'une 'BackedList' au lieu d'une 'List' normale. À toutes fins pratiques, la 'BackedList' peut être vue comme une autre 'List' du code client.
Cela a du sens du point de vue d'une grande classe principale qui pourrait donner accès à un ou plusieurs composants de liste qui eux-mêmes sont soutenus par un ou plusieurs schémas dans une base de données. Étant donné que les méthodes C # ne sont pas virtuelles par défaut, la liste fournie par la classe principale ne peut pas être un simple IEnumerable ou ICollection ou même une instance de List mais doit à la place être annoncée au client en tant que 'BackedList' afin de garantir que la nouvelle version de l'opération "Ajouter" est appelée pour mettre à jour le schéma correct.
Ce n'est certainement pas un problème de performance. Sun Java utilise le même code pour distribuer (invokevirtual
bytecode) et HotSpot génère exactement le même code, que final
ou non. Je crois que tous les objets C # ( mais pas les structures) ont des méthodes virtuelles, vous aurez donc toujours besoin de l'identification de classe vtbl
/runtime. C # est un dialecte de "langages de type Java". Suggérer que cela vient de C++ n'est pas tout à fait honnête.
Il y a une idée que vous devriez "concevoir pour l'héritage ou bien l'interdire". Ce qui semble être une excellente idée jusqu'au moment où vous avez une analyse de rentabilisation sévère à apporter dans une solution rapide. Peut-être héritant du code que vous ne contrôlez pas.