Contrairement à Java, pourquoi C # traite-t-il les méthodes comme des fonctions non virtuelles par défaut? Est-ce 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 de l'architecture existante. Mais qu'en est-il des effets secondaires? Est-ce vraiment un bon compromis d'avoir des méthodes non virtuelles par défaut?
c#
java
.net
virtual-functions
Burcu Dogan
la source
la source
this
référence est nulle. Voir ici pour plus d'informations.Réponses:
Les classes doivent être conçues pour l'héritage afin de pouvoir 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û êtresealed
par défaut.virtual
Les méthodes peuvent également avoir une légère implication sur les performances. Ce ne sera probablement pas la raison principale, cependant.la source
callvirt
par un simplecall
dans le code IL (ou même plus en aval lors de JITting). Java HotSpot fait de même. Le reste de la réponse est juste.Je suis surpris qu'il semble y avoir un tel consensus ici que le 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 me lisent comme le vieil 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 sa bibliothèque pour l'héritage et / ou l'extensibilité est le codeur qui a produit exactement la bibliothèque que je devrai probablement corriger ou modifier - exactement le bibliothèque où la possibilité de remplacer serait la plus utile.
Le nombre de fois où j'ai eu à é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 remplaçant là où le concepteur n'aurait peut-être pas pensé que je pourrais.
Le non-virtuel par défaut me rend la vie plus difficile.
MISE À JOUR: Il a été souligné [tout à fait correctement] que je n'ai pas vraiment répondu à la question. Donc - et avec des excuses pour être plutôt en retard ...
Je voulais en quelque sorte être capable d'écrire quelque chose de concis comme "C # implémente des méthodes comme non virtuelles par défaut parce qu'une mauvaise décision a été prise qui valorisait plus les programmes que les programmeurs". (Je pense que cela pourrait être quelque peu justifié sur la base de certaines des autres réponses à cette question - comme la performance (optimisation prématurée, n'importe qui?), Ou la garantie du comportement des classes.)
Cependant, je me rends compte que je ne ferais que donner mon opinion et non la réponse définitive que Stack Overflow souhaite. Sûrement, j'ai pensé, au plus haut niveau, la réponse définitive (mais inutile) est:
Ils ne sont pas virtuels par défaut car 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, nous ne ... oh, attendez! La transcription d'une conversation!
Il semblerait donc que les réponses et les commentaires ici sur les dangers de la substitution des API et la nécessité de concevoir explicitement pour l'héritage sont sur la bonne voie, mais manquent tous un aspect temporel important: la principale préoccupation d'Anders était de maintenir implicite une classe ou une API. contrat entre les versions . Et je pense qu'il est en fait plus préoccupé par le fait de permettre à la plate-forme .Net / C # de changer sous le code plutôt que par le changement du code utilisateur au-dessus de la plate-forme. (Et son point de vue «pragmatique» est l'exact opposé du mien parce qu'il regarde de l'autre côté.)
(Mais ne pourraient-ils pas simplement avoir choisi virtuel par défaut, puis "final" parsemé de 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, alors je vais laisser tomber.)
la source
virtual
... Complément Visual Studio n'importe qui?Parce qu'il est trop facile d'oublier qu'une méthode peut être remplacée et ne pas être 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 scellées par défaut.
la source
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.
3- 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 méthode getter simple fonctionnera 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 devriez faire le méthode et / ou la classe finale).
On peut se demander, comme Zifre l'a mentionné, pourquoi le langage C # n'est pas allé plus loin et a 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.
la source
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: "À quelle fréquence implémentez-vous des classes qui sont membres d'une classe hiearchy?". L'encombrement mémoire est une autre raison pour éviter d'activer la répartition dynamique par défaut. Une classe sans pointeur virtuel (vpointer) pointant vers une table virtuelle , est bien sûr plus petite que la classe correspondante avec la liaison tardive activée.
Le problème des performances n'est pas si facile de dire «oui» ou «non». La raison en est la compilation Just In Time (JIT) qui est une optimisation d'exécution en C #.
Une autre question similaire sur la " vitesse des appels virtuels. "
la source
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 prévoir 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 change 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.
la source
Si toutes les méthodes C # étaient virtuelles, le vtbl serait beaucoup plus gros.
Les objets C # ont uniquement des méthodes virtuelles 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, seules les méthodes Object de base seront présentes.
@Tom Hawtin: Il est probablement plus exact de dire que C ++, C # et Java appartiennent tous à la famille des langages C :)
la source
Venant d'un arrière-plan perl, je pense que C # a scellé la perte de tout développeur qui aurait pu vouloir étendre et modifier le comportement d'une classe de base via 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. Et si un développeur souhaitait mettre à jour l'une des nombreuses bases de données potentielles chaque fois qu'une liste particulière est «ajoutée» à? Si 'Add' était virtuel par défaut, le développeur pourrait développer une classe 'BackedList' qui écrasait 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 considérée comme une simple «liste» à partir du code client.
Cela a du sens du point de vue d'une grande classe principale qui pourrait fournir l'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 publié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.
la source
B
c'est le casA
. SiB
nécessite quelque chose de différent,A
ce n'est pas le casA
. Je crois que le dépassement en tant que capacité dans le langage lui-même est un défaut de conception. Si vous avez besoin d'uneAdd
méthode différente , votre classe de collection n'est pas unList
. Essayer de dire que c'est truquer. La bonne approche ici est la composition (et non la simulation). Il est vrai que tout le cadre est construit sur des capacités primordiales, mais je n'aime pas ça.Ce n'est certainement pas un problème de performance. L'interpréteur Java de Sun utilise le même code pour distribuer (
invokevirtual
bytecode) et HotSpot génère exactement le même code que ce soitfinal
ou non. Je crois que tous les objets C # (mais pas les structs) ont des méthodes virtuelles, vous aurez donc toujours besoin de l'vtbl
identification de classe / runtime. C # est un dialecte de "langages de type Java". Suggérer qu'il 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érieuse à mettre en place rapidement. Peut-être héritant d'un code que vous ne contrôlez pas.
la source
final
lesfinal
méthodes et les méthodes efficaces est triviale. Il est également intéressant de noter qu'il peut faire de l'inlining bimorphique, c'est-à-dire des méthodes en ligne avec deux implémentations différentes.