Le downcasting signifie la conversion d'une classe de base (ou interface) vers une classe sous-classe ou feuille.
Un exemple de downcast pourrait être si vous lancez à partir System.Object
d'un autre type.
Le downcasting est impopulaire, peut-être une odeur de code: la doctrine orientée objet consiste à préférer, par exemple, la définition et l’appel de méthodes virtuelles ou abstraites au lieu d’un downcasting.
- Quels sont, le cas échéant, des cas d'utilisation corrects et appropriés pour le downcasting? C'est-à-dire, dans quelles circonstances est-il approprié d'écrire du code qui est downscast?
- Si votre réponse est "néant", alors pourquoi le downcasting est-il pris en charge par la langue?
c#
object-oriented
ChrisW
la source
la source
ToString
); son autre exemple est "conteneurs Java" car Java ne supporte pas les génériques (ce que C # fait). stackoverflow.com/questions/1524197/downcast-and-upcast dit ce downcasting est , mais sans exemples quand il est approprié .before Java had support for generics
, nonbecause Java doesn't support generics
. Et amon vous a à peu près donné la réponse - lorsque le système de types avec lequel vous travaillez est trop restrictif et que vous n’êtes pas en mesure de le modifier.Réponses:
Voici quelques utilisations appropriées du downcasting.
Et je respectueusement avec véhémence en désaccord avec d' autres ici qui disent l'utilisation de downcasts est certainement une odeur de code, parce que je crois qu'il n'y a pas d' autre moyen raisonnable de résoudre les problèmes correspondants.
Equals
:Clone
:Stream.EndRead/Write
:Si vous dites que ce sont des odeurs de code, vous devez leur proposer de meilleures solutions (même si elles sont évitées en raison d'inconvénients). Je ne crois pas qu'il existe de meilleures solutions.
la source
object.Equals
correspond bien à cette description (essayez de fournir correctement un contrat d'égalité dans une super-classe qui permet aux sous-classes de le non-trivial). En tant que programmeurs d'applications, nous ne pouvons pas le résoudre (dans certains cas, nous pouvons l'éviter) est un sujet différent.Object.Equals is horrid. Equality computation in C# is completely messed up
(commentaire). (Commentaire d'Eric sur sa réponse). C'est drôle de ne pas vouloir reconsidérer son opinion, même lorsqu'un des concepteurs principaux du langage lui-même vous dit que vous vous trompez.Je ne suis pas d'accord. Le downcasting est extrêmement populaire ; un très grand nombre de programmes du monde réel contiennent une ou plusieurs diffusions à la baisse. Et ce n'est peut- être pas une odeur de code. C'est vraiment une odeur de code. C'est pourquoi l'opération de downcasting doit être manifeste dans le texte du programme . C’est pour que vous puissiez plus facilement remarquer l’odeur et consacrer votre attention à la révision de code.
Dans toutes les circonstances où:
Si vous pouvez refactoriser à moindre coût un programme afin que soit le compilateur puisse déduire le type d'exécution, soit le refactoriser afin que vous n'ayez pas besoin de la capacité du type le plus dérivé, faites-le. Des abaissements ont été ajoutés à la langue dans les cas où il est difficile et coûteux de restructurer le programme.
C # a été inventé par des programmeurs pragmatiques ayant des tâches à accomplir, pour des programmeurs pragmatiques ayant des tâches à accomplir. Les concepteurs C # ne sont pas des puristes OO. Et le système de type C # n’est pas parfait; de par sa conception, il sous-estime les restrictions pouvant être imposées au type d’exécution d’une variable.
De plus, le downcasting est très sûr en C #. Nous avons une forte garantie que le downcast sera vérifié au moment de l'exécution. S'il ne peut pas être vérifié, le programme fait le bon choix et se bloque. C'est merveilleux; cela signifie que si votre compréhension correcte à 100% de la sémantique des types s'avère correcte à 99,99%, votre programme fonctionne 99,99% du temps et se bloque le reste du temps, au lieu de se comporter de manière imprévisible et de corrompre les données utilisateur. .
EXERCICE: Il existe au moins un moyen de produire un downcast en C # sans utiliser d'opérateur de distribution explicite. Pouvez-vous penser à de tels scénarios? Etant donné que ce sont aussi des odeurs potentielles de code, quels facteurs ont été à l’origine de la conception d’une fonctionnalité susceptible de provoquer un plantage sans affichage du code dans le code?
la source
Object.Equals
est horrible . Le calcul de l'égalité en C # est complètement foiré , beaucoup trop foutu pour l'expliquer dans un commentaire. Il y a tellement de façons de représenter l'égalité. il est très facile de les rendre incompatibles; il est très facile, en effet nécessaire de violer les propriétés requises d'un opérateur d'égalité (symétrie, réflexivité et transitivité). Il n'y aurait pas la conception d' un si je étais système de type à partir de zéro aujourd'huiEquals
(ouGetHashcode
ouToString
) surObject
du tout.Les gestionnaires d'événements ont généralement la signature
MethodName(object sender, EventArgs e)
. Dans certains cas, il est possible de gérer l'événement sans tenir compte du typesender
, voire de ne pas utilisersender
du tout, mais dans d'autres cas, ilsender
doit être converti en un type plus spécifique pour gérer l'événement.Par exemple, vous pouvez avoir deux
TextBox
s et utiliser un seul délégué pour gérer un événement de chacun d'eux. Ensuite, vous devez transtypersender
en unTextBox
afin de pouvoir accéder aux propriétés nécessaires pour gérer l'événement:Oui, dans cet exemple, vous pouvez créer votre propre sous-classe,
TextBox
qui l’a fait en interne. Pas toujours pratique ou possible, cependant.la source
TextChanged
événement est un membre deControl
- je me demande pourquoi le typesender
n'est pasControl
au lieu de typeobject
? De plus, je me demande si (théoriquement) le cadre aurait pu redéfinir l'événement pour chaque sous-classe (afin que, par exemple,TextBox
une nouvelle version de l'événement puisse être utilisée, en utilisantTextBox
le type d'expéditeur) ... qui pourrait (ou pourrait ne pas) nécessiter un downcasting ( toTextBox
) à l'intérieur de l'TextBox
implémentation (pour implémenter le nouveau type d'événement), mais éviterait de demander un downcast dans le gestionnaire d'événements du code de l'application.TextBox sender
plutôt que de neobject sender
pas trop compliquer le code, je suis sûr que cela serait fait.Vous avez besoin d'un downcasting lorsque quelque chose vous donne un super-type et que vous devez le gérer différemment en fonction du sous-type.
Non, ça ne sent pas bon. Dans un monde parfait, il
Donation
y aurait une méthode abstraitegetValue
et chaque sous-type implémenté aurait une implémentation appropriée.Mais que se passe-t-il si ces classes proviennent d'une bibliothèque que vous ne pouvez pas ou ne voulez pas changer? Alors il n'y a pas d'autre option.
la source
dynamic
mot clé qui donne le même résultat.void accept(IDonationVisitor visitor)
, que ses sous-classes implémentent en appelant des méthodes spécifiques (surchargées, par exemple)IDonationVisitor
.dynamic
mot - clé.dynamic
toujours en baisse , juste plus lent.Pour ajouter à la réponse d'Eric Lippert puisque je ne peux pas commenter ...
Vous pouvez souvent éviter le downcasting en restructurant une API pour qu'elle utilise des génériques. Mais les génériques n’ont pas été ajoutés à la langue jusqu’à la version 2.0 . Ainsi, même si votre propre code n'a pas besoin de prendre en charge les versions de langue ancienne, vous pouvez vous retrouver à utiliser des classes héritées qui ne sont pas paramétrées. Par exemple, certaines classes peuvent être définies pour porter une charge utile de type
Object
, que vous êtes obligé de transtyper vers le type que vous connaissez réellement.la source
Il existe un compromis entre les langages statiques et à typage dynamique. Le typage statique fournit au compilateur de nombreuses informations lui permettant de donner des garanties assez fortes quant à la sécurité des (parties de) programmes. Cela a toutefois un coût, car non seulement vous devez savoir que votre programme est correct, mais vous devez écrire le code approprié pour convaincre le compilateur que tel est le cas. En d’autres termes, il est plus facile de faire des réclamations que de les prouver.
Il existe des constructions "non sûres" qui vous aident à faire des assertions non prouvées au compilateur. Par exemple, appels inconditionnels à
Nullable.Value
, downcasts,dynamic
objets, etc. , sans condition . Ils vous permettent d'affirmer une revendication ("J'affirme que l'objeta
est unString
, si je me trompe, jette unInvalidCastException
") sans qu'il soit nécessaire de le prouver. Cela peut être utile dans les cas où il est beaucoup plus difficile de prouver que cela vaut la peine.Abuser de cela est risqué, c'est précisément pourquoi la notation explicite à la baisse existe et est obligatoire; c'est un sel syntaxique destiné à attirer l'attention sur une opération non sécurisée. Le langage aurait pu être implémenté avec un downcasting implicite (où le type inféré est sans ambiguïté), mais cela masquerait cette opération non sécurisée, ce qui n'est pas souhaitable.
la source
La décadence est considérée comme mauvaise pour plusieurs raisons. Principalement, je pense parce que c'est anti-POO.
OOP aimerait vraiment si vous n’avez jamais à baisser les bras, car sa raison d’être est que le polymorphisme signifie que vous n’êtes pas obligé de partir.
tu fais juste
et le code fait comme par magie la bonne chose.
Il y a des raisons «difficiles» à ne pas abaisser:
Mais l’alternative au downcasting dans certains scénarios peut être assez laide.
L'exemple classique est le traitement des messages, dans lequel vous ne souhaitez pas ajouter la fonction de traitement à l'objet, mais la conserver dans le processeur de messages. J'ai alors une tonne de données
MessageBaseClass
dans un tableau à traiter, mais j'ai également besoin de chaque sous-type pour un traitement correct.Vous pouvez utiliser Double Dispatch pour résoudre le problème ( https://en.wikipedia.org/wiki/Double_dispatch ) ... Mais cela a aussi ses problèmes. Vous pouvez également vous contenter d'un code simple au risque de ces raisons difficiles.
Mais c'était avant l'invention des génériques. Maintenant, vous pouvez éviter le downcasting en fournissant un type où les détails spécifiés ultérieurement.
MessageProccesor<T>
peut modifier les paramètres de sa méthode et retourner des valeurs en fonction du type spécifié, tout en fournissant une fonctionnalité génériqueBien sûr, personne ne vous oblige à écrire du code POO et de nombreuses fonctionnalités linguistiques sont fournies, mais mal vues, telles que la réflexion.
la source
static_cast
au lieu dedynamic_cast
.lParam
sur quelque chose de spécifique. Je ne suis cependant pas sûr que ce soit un bon exemple en C #.static_cast
c'est toujours rapide ... mais il n'est pas garanti de ne pas échouer de manière indéfinie si vous commettez une erreur et que le type n'est pas ce que vous attendez.En plus de tout ce qui a été dit précédemment, imaginez une propriété Tag de type objet. Il vous permet de stocker un objet de votre choix dans un autre objet pour une utilisation ultérieure, selon vos besoins. Vous avez besoin d'abaisser cette propriété.
En général, ne pas utiliser quelque chose jusqu'à ce jour n'est pas toujours une indication d'inutile ;-)
la source
Control
(au lieu d’utiliser uneobject Tag
propriété) serait de créer un répertoireDictionary<Control, string>
dans lequel stocker l’étiquette pour chaque contrôle.Tag
est une propriété publique d'une classe Framework .Net, ce qui montre que vous êtes (plus ou moins) censé l'utiliser.System.Collections.ArrayList
) ou dépréciées (par exempleIEnumerator.Reset
).L'utilisation la plus commune de la décroissance dans mon travail est due à un idiot qui a brisé le principe de substitution de Liskov.
Imaginez qu'il y ait une interface dans une bibliothèque tierce.
Et 2 classes qui l'implémentent.
Bar
etQux
.Bar
est bien comporté.Mais
Qux
ne se comporte pas bien.Eh bien ... maintenant je n'ai pas le choix. Je dois taper check et (éventuellement) downcast afin d'éviter que mon programme ne s'arrête brusquement.
la source
var qux = foo as Qux;
.WPF est un autre exemple concret
VisualTreeHelper
. Il utilise downcast pour lancerDependencyObject
versVisual
etVisual3D
. Par exemple, VisualTreeHelper.GetParent . Le downcast se produit dans VisualTreeUtils.AsVisualHelper :la source