web-dev-qa-db-fra.com

Vous voulez toutes les méthodes virtuelles?

En Java, vous pouvez marquer la méthode en tant que final pour le rendre impossible à remplacer.

En C #, vous devez marquer la méthode comme virtuelle pour pouvoir remplacer possible .

Cela signifie-t-il qu'en C #, vous devez marquer toutes les méthodes comme virtuelles (à l'exception de quelques-unes que vous ne souhaitez pas remplacer), car vous ne savez probablement pas comment votre classe peut être héritée?

70
Georgii Oleinikov

En C #, vous devez marquer la méthode comme virtuelle pour pouvoir la remplacer. Cela signifie-t-il qu'en C #, vous devez marquer toutes les méthodes comme virtuelles (à l'exception de quelques-unes que vous ne souhaitez pas remplacer), car vous ne savez probablement pas comment votre classe peut être héritée?

Non. Si les concepteurs de langage pensaient que le virtuel aurait dû être le paramètre par défaut, alors ce serait le paramètre par défaut .

La surcapacité est une fonctionnalité et, comme toutes les fonctionnalités, elle a coûte. Les coûts d'une méthode pouvant être remplacée sont considérables: les coûts de conception, de mise en œuvre et de test sont élevés, en particulier s'il existe une "sensibilité" à la classe; Les méthodes virtuelles sont des moyens d'introduire du code tiers non testé dans un système et qui ont un impact sur la sécurité.

Si vous ne savez pas comment vous voulez que votre classe soit héritée, alors ne publiez pas votre classe car vous n'avez pas encore fini de la concevoir. Votre modèle d’extensibilité est définitivement quelque chose que vous devriez connaître à l’avance; cela devrait profondément influencer votre conception et votre stratégie de test. 

Je préconise que toutes les classes soient scellées et que toutes les méthodes soient non-virtuelles jusqu'à ce que vous disposiez d'une raison concrète, axée sur le client, de décacheter ou de rendre une méthode virtuelle. 

En gros, votre question est la suivante: "Je ne sais pas comment mes clients envisagent de consommer ma classe; devrais-je donc la rendre arbitrairement extensible?" Non; vous devriez devenir informé! Vous ne demanderiez pas "Je ne sais pas comment mes clients vont utiliser ma classe, alors devrais-je faire en sorte que toutes mes propriétés soient en lecture-écriture? Et devrais-je faire en sorte que toutes mes méthodes soient en lecture-écriture des propriétés de type délégué peut remplacer n'importe quelle méthode avec leur propre implémentation? " Non, ne faites aucune de ces choses avant d'avoir la preuve qu'un utilisateur a réellement besoin de cette capacité! Consacrez un temps précieux à la conception, au test et à la mise en œuvre des fonctionnalités que les utilisateurs souhaitent et dont ils ont réellement besoin, et ce, en toute connaissance de cause.

135
Eric Lippert

A mon avis, le réponse actuellement accepté est inutilement dogmatique.

Le fait est que lorsque vous ne marquez pas une méthode avec virtual, les autres ne peuvent pas redéfinir son comportement et lorsque vous marquez une classe comme sealed, les autres ne peuvent pas hériter de la classe. Cela peut causer une douleur importante. Je ne sais pas combien de fois j'ai maudit une API pour marquer les classes sealed ou ne pas marquer les méthodes virtual simplement parce qu'elles ne prévoyaient pas mon cas d'utilisation.

Théoriquement, il serait peut-être judicieux de n'autoriser que les méthodes de contournement et les classes héritées censées être remplacées et héritées, mais en pratique, il est impossible de prévoir tous les scénarios possibles et il n'y a pas vraiment de bonne raison d'être aussi fermé.

  1. Si vous n'avez pas une très bonne raison, ne marquez pas les classes comme étant sealed
  2. Si votre bibliothèque est censée être consommée par d'autres, essayez au moins de marquer les méthodes principales d'une classe contenant le behavior comme étant une variable virtual.

Une façon de faire l'appel consiste à examiner le nom de la méthode ou de la propriété. Une méthode GetLength () sur une liste fait exactement ce que son nom implique et ne permet pas une grande partie de interpretation. Changer son implémentation ne serait probablement pas très transparent, donc le marquer comme virtual est probablement inutile. Marquer la méthode Add comme virtuelle est beaucoup plus utile car quelqu'un pourrait créer une liste spéciale qui n'accepte que certains objets via la méthode Add, etc. Un autre exemple est le contrôle personnalisé. Vous voudriez créer la méthode de dessin principale virtual pour que les autres utilisateurs puissent utiliser l'essentiel du comportement et changer simplement l'apparence, sans pour autant redéfinir les propriétés X et Y.

En fin de compte, vous n'avez souvent pas à prendre cette décision immédiatement. Dans un projet interne où vous pouvez facilement changer le code de toute façon, je ne m'inquiéterais pas de ces choses. Si une méthode doit être remplacée, vous pouvez toujours la rendre virtuelle lorsque cela se produit .. Au contraire, si le projet est une API ou une bibliothèque consommée par d'autres et lente à mettre à jour, il est certainement utile de penser à laquelle les classes et les méthodes peuvent être utiles. Dans ce cas, je pense qu'il est préférable d'être ouvert plutôt que strictement fermé.

24
Patrick Klug

Non! Parce que vous ne savez pas comment votre classe sera héritée, vous devriez seulement marquer une méthode avec virtual si vous savez que vous voulez qu'elle soit remplacée.

22
John Saunders

Non. Seules les méthodes que vous souhaitez que les classes dérivées spécifient doivent être virtuelles. 

Le virtuel n'est pas lié à la finale.

Pour éviter de surcharger une méthode virtuelle en c #, utilisez sealed

public class MyClass
{
    public sealed override void MyFinalMethod() {...}
}
5
nunespascal

Nous pouvons évoquer des raisons pour/encore l'un ou l'autre camp, mais c'est totalement inutile.

En Java, il existe des millions de méthodes publiques non finales non voulues, mais nous entendons très peu d'histoires d'horreur.

En C #, il existe des millions de méthodes publiques scellées et nous entendons très peu d'histoires d'horreur.

Ce n’est donc pas un gros problème: la nécessité de remplacer une méthode publique est rare;.


Cela me rappelle un autre argument - si une variable locale doit être finale par défaut. C'est une très bonne idée, mais nous ne pouvons pas exagérer sa valeur. Des milliards de variables locales pourraient être, mais ne le sont pas, définitives, mais il s’est avéré que c’était un problème réel. 

4
irreputable

Rendre une méthode virtuelle ralentira généralement le code qui doit l'appeler. Ce ralentissement sera insignifiant, mais peut dans certains cas être assez important (notamment parce que les appels de méthode non virtuels peuvent être alignés, ce qui peut permettre à l'optimiseur d'éliminer les opérations inutiles). Il n’est pas toujours possible de prédire dans quelle mesure les appels virtuels peuvent affecter la vitesse d’exécution, et il est généralement préférable d’annuler des actions qui ralentiront le code, sauf s’il ya un avantage à le faire.

L’avantage en termes de performances de rendre des méthodes non virtuelles est probablement suffisant dans de nombreux cas pour justifier que les méthodes soient non virtuelles par défaut, mais lorsque les classes sont conçues pour être héritées, la plupart des méthodes doivent être virtuelles et non scellées; l'utilisation principale des méthodes non virtuelles ou scellées doit être utilisée comme wrappers pour d'autres méthodes virtuelles (éventuellement protégées) (le code qui souhaite modifier le comportement sous-jacent doit remplacer le virtuel approprié plutôt que le wrapper).

Il existe souvent des raisons non liées à la performance pour marquer des classes comme sealed ou pour limiter l'héritage à d'autres classes au sein de l'Assemblée. Entre autres choses, si une classe peut être héritée de manière externe, tous les membres ayant une étendue protected sont effectivement ajoutés à son API publique et toute modification de leur comportement dans la classe de base peut rompre toute classe dérivée qui repose sur ce comportement. D'un autre côté, si une classe est héritable, rendre ses méthodes virtual n'augmente pas vraiment son exposition. En tout état de cause, cela peut réduire la dépendance de la classe dérivée sur les internes de la classe de base en leur permettant d’enterrer complètement les aspects de l’implémentation de la classe de base qui ne sont plus pertinents dans la classe dérivée [par ex. si les membres de List<T> étaient virtuels, une classe dérivée qui les surchargerait tous pourrait utiliser un tableau de tableaux pour contenir des objets (en évitant les problèmes d'objet de taille importante), et n'aurait pas à essayer de garder le tableau privé utilisé par List<T> cohérent avec le tableau de tableaux.

1
supercat

Non, vous ne devez pas marquer toutes les méthodes comme virtuelles. Vous devriez envisager comment votre classe pourrait être héritée. Si la classe ne doit pas être héritée, marquez-la comme scellée et les membres ne doivent évidemment pas être virtuels. Si votre classe risque d’être héritée, optimisez la possibilité de redéfinir le comportement. Par conséquent, utilisez généreusement le virtuel partout dans ces classes, sauf si vous avez une raison de ne pas le faire.

0
Elliott Anderson