108 votes

Pourquoi faut-il éviter le gaspillage ?

Je tends généralement à éviter autant que possible de convertir les types car j'ai l'impression que c'est une mauvaise pratique de codage et que cela peut entraîner une pénalité de performance.

Mais si quelqu'un me demandait d'expliquer précisément pourquoi c'est le cas, je les regarderais probablement comme un cerf pris dans les phares.

Alors pourquoi/quand la conversion est-elle mauvaise?

Est-ce général pour Java, C#, C++ ou chaque environnement d'exécution différent gère-t-il cela selon ses propres termes?

Les spécificités pour n'importe quel langage sont les bienvenues, par exemple pourquoi est-ce mauvais en C++?

4 votes

D'où as-tu tiré cette impression?

0 votes

Pour être plus précis, qu'est-ce qui se passe dans le mécanisme d'exécution qui le rend si mauvais, comment fonctionne la conversion au niveau de la machine?

10 votes

J'ai cette impression car je n'ai jamais lu un livre ou rencontré un programmeur qui a dit "CASTING SOOOO GOOOOD!!!".

153voto

Jerry Coffin Points 237758

Vous avez tagué ceci avec trois langues et les réponses sont vraiment très différentes entre les trois. La discussion sur C++ implique plus ou moins la discussion sur les cast en C également, et cela donne (plus ou moins) une quatrième réponse.

Puisque c'est celui que vous n'avez pas mentionné explicitement, je vais commencer par C. Les cast en C posent un certain nombre de problèmes. L'un d'eux est qu'ils peuvent faire différentes choses. Dans certains cas, le cast ne fait rien de plus que dire au compilateur (en substance) : "tais-toi, je sais ce que je fais" - c'est-à-dire, il garantit que même lorsque vous effectuez une conversion qui pourrait poser problème, le compilateur ne vous avertira pas de ces problèmes potentiels. Par exemple, char a=(char)123456;. Le résultat exact de ceci est défini par l'implémentation (dépend de la taille et du signe de char), et sauf dans des situations assez étranges, ce n'est probablement pas utile. Les cast en C varient également selon qu'ils sont quelque chose qui se passe seulement au moment de la compilation (c'est-à-dire, vous dites juste au compilateur comment interpréter/traiter des données) ou quelque chose qui se passe au moment de l'exécution (par exemple, une conversion réelle de double à long).

C++ tente de traiter cela au moins dans une certaine mesure en ajoutant un certain nombre de nouveaux opérateurs de cast, chacun étant limité à seulement une partie des capacités d'un cast en C. Cela rend plus difficile (par exemple) de faire accidentellement une conversion que vous n'aviez vraiment pas l'intention de faire - si vous avez l'intention uniquement de supprimer la constance d'un objet, vous pouvez utiliser const_cast, et être sûr que la seule chose qu'il peut affecter est que l'objet est const, volatile, ou non. Inversement, un static_cast n'est pas autorisé à affecter si un objet est const ou volatile. En bref, vous avez la plupart des mêmes types de capacités, mais ils sont catégorisés de sorte qu'un cast peut généralement uniquement effectuer un type de conversion, tandis qu'un cast de style C peut effectuer deux ou trois conversions en une seule opération. L'exception principale est que vous pouvez utiliser un dynamic_cast à la place d'un static_cast dans au moins certains cas et malgré le fait qu'il soit écrit comme un dynamic_cast, il se terminera vraiment comme un static_cast. Par exemple, vous pouvez utiliser un dynamic_cast pour remonter ou descendre une hiérarchie de classes - mais un cast "vers le haut" dans la hiérarchie est toujours sûr, donc il peut être fait de manière statique, tandis qu'un cast "vers le bas" dans la hiérarchie n'est pas nécessairement sûr et est donc effectué dynamiquement.

Java et C# se ressemblent beaucoup plus. En particulier, avec ces deux langages, le cast est (virtuellement ?) toujours une opération en temps d'exécution. En termes des opérateurs de cast C++, c'est généralement plus proche d'un dynamic_cast en termes de ce qui est vraiment fait - c'est-à-dire, lorsque vous tentez de caster un objet vers un certain type cible, le compilateur insère une vérification en temps d'exécution pour voir si cette conversion est autorisée, et lance une exception si ce n'est pas le cas. Les détails exacts (par exemple, le nom utilisé pour l'exception de "mauvais cast") varient, mais le principe de base reste principalement similaire (bien que, si ma mémoire est bonne, Java rend les cast appliqués aux rares types non-objets comme int beaucoup plus proche des cast en C - mais ces types sont utilisés assez rarement pour que 1) je ne m'en souvienne pas avec certitude, et 2) même si c'est vrai, cela n'importe pas beaucoup de toute façon).

En regardant les choses de manière plus générale, la situation est assez simple (du moins à mon avis) : un cast (évidemment) signifie que vous convertissez quelque chose d'un type à un autre. Quand/si vous faites cela, cela soulève la question "Pourquoi ?" Si vous voulez vraiment que quelque chose soit d'un type particulier, pourquoi ne l'avez-vous pas défini comme étant ce type dès le départ ? Cela ne veut pas dire qu'il n'y a jamais de raison de faire une telle conversion, mais à chaque fois que cela se produit, cela devrait susciter la question de savoir si vous pourriez revoir la conception du code pour que le bon type soit utilisé tout au long. Même les conversions en apparence inoffensives (par exemple, entre un entier et un point flottant) devraient être examinées beaucoup plus attentivement qu'il n'est habituel de le faire. Malgré leur ressemblance apparente, les entiers devraient vraiment être utilisés pour les types de choses "comptées" et les nombres à virgule flottante pour les types de choses "mesurées". Ignorer la distinction est ce qui conduit à certaines affirmations folles comme "la famille américaine moyenne a 1,8 enfants". Même si nous comprenons tous comment cela arrive, le fait est qu'aucune famille n'a 1,8 enfants. Ils peuvent avoir 1 ou 2 ou plus que cela - mais jamais 1,8.

13 votes

On dirait que vous souffrez d'un 'mort par le manque d'attention' ici, c'est une bonne réponse à mon avis.

2 votes

Une réponse solide, mais quelques parties des "Je ne sais pas" pourraient être resserrées pour la rendre plus exhaustive. @Dragontamer5788 c'est une bonne réponse, mais pas exhaustive.

6 votes

Un enfant entier et un enfant avec une jambe manquante seraient 1,8 enfants. Mais une réponse très gentille quand même. :)

50voto

Eric Lippert Points 300275

Beaucoup de bonnes réponses ici. Voici comment je vois les choses (d'un point de vue C#).

Le cast signifie généralement l'une des deux choses suivantes :

  • Je connais le type d'exécution de cette expression mais le compilateur ne le sait pas. Compilateur, je te dis que, à l'exécution, l'objet correspondant à cette expression sera vraiment de ce type. À partir de maintenant, sache que cette expression doit être traitée comme étant de ce type. Générer du code qui suppose que l'objet sera du type donné, ou lever une exception si j'ai tort.

  • Le compilateur et le développeur connaissent le type d'exécution de l'expression. Il y a une autre valeur d'un type différent associée à la valeur que cette expression aura à l'exécution. Générer du code qui produit la valeur du type désiré à partir de la valeur du type donné; si vous ne pouvez pas le faire, alors lancez une exception.

Remarquez que ce sont des opposés. Il y a deux types de cast! Il y a des casts où vous donnez un indice au compilateur sur la réalité - hé, cette chose de type objet est en fait de type Client - et il y a des casts où vous dites au compilateur d'effectuer un mappage d'un type à un autre - hé, j'ai besoin de l'int correspondant à ce double.

Les deux types de cast sont des signaux d'alarme. Le premier type de cast pose la question "pourquoi exactement le développeur sait quelque chose que le compilateur ne sait pas ?" Si vous êtes dans cette situation alors il vaut généralement mieux changer le programme afin que le compilateur ait une emprise sur la réalité. Alors vous n'avez pas besoin du cast; l'analyse est faite au moment de la compilation.

Le deuxième type de cast pose la question "pourquoi l'opération n'est-elle pas réalisée dans le type de données cible dès le départ ?" Si vous avez besoin d'un résultat en int alors pourquoi détenez-vous un double en premier lieu ? Ne devriez-vous pas détenir un int ?

Quelques réflexions supplémentaires ici:

Lien

2 votes

Je ne pense pas que ce sont des drapeaux rouges inhérents. Si vous avez un objet Foo qui hérite de Bar, et que vous le stockez dans une List, alors vous aurez besoin de castings si vous voulez récupérer ce Foo. Peut-être que cela indique un problème au niveau architectural (pourquoi stockons-nous des Bar au lieu de des Foo?), mais pas nécessairement. Et, si ce Foo a également un cast valide vers int, cela répond aussi à votre autre commentaire: vous stockez un Foo, pas un int, car un int n'est pas toujours approprié.

6 votes

@Mike Caron - Je ne peux pas répondre pour Eric, évidemment, mais pour moi un drapeau rouge signifie "c'est quelque chose à penser", pas "c'est quelque chose de mal". Et il n'y a aucun problème à stocker un Foo dans une List, mais au moment de la conversion, vous essayez de faire quelque chose avec Foo qui n'est pas approprié pour un Bar. Cela signifie que des comportements différents pour les sous-types sont réalisés à travers un mécanisme autre que le polymorphisme intégré fourni par les méthodes virtuelles. Peut-être que c'est la bonne solution, mais le plus souvent c'est un drapeau rouge.

16 votes

En effet. Si vous extrayez des éléments d'une liste d'animaux et que vous devez ensuite dire au compilateur, au fait, je sais que le premier est un tigre, le deuxième est un lion et le troisième est un ours, alors vous auriez dû utiliser un Tuple, pas une Liste.

37voto

Mike Miller Points 1530

Les erreurs de casting sont toujours signalées comme des erreurs d'exécution en java. L'utilisation de génériques ou de modèles transforme ces erreurs en erreurs de compilation, ce qui rend beaucoup plus facile de détecter quand vous avez fait une erreur.

Comme je l'ai dit ci-dessus. Cela ne veut pas dire que tous les castings sont mauvais. Mais s'il est possible de l'éviter, il vaut mieux le faire.

4 votes

Cela suppose que tout cast est "mauvais"; cependant, ce n'est pas entièrement vrai. Prenez, par exemple, C#, avec à la fois un support pour les casts implicites et explicites. Le problème survient lorsqu'un cast est effectué qui enlève (accidentellement) des informations ou de la sûreté de type (ceci varie selon le langage).

5 votes

En fait, c'est complètement faux en C++. Peut-être qu'une modification pour inclure le(s) langage(s) ciblé(s) par ces informations est nécessaire.

0 votes

@Erick - Je le ferais, mais je ne connais pas grand chose à Java, ni suffisamment de détails sur C# pour être certain que les informations sont correctes.

19voto

Steve Townsend Points 36948

Le casting n'est pas intrinsèquement mauvais, c'est juste qu'il est souvent mal utilisé comme un moyen d'atteindre quelque chose qui ne devrait vraiment pas être fait du tout, ou qui devrait l'être de manière plus élégante.

Si c'était universellement mauvais, les langages ne le soutiendraient pas. Comme toute autre fonctionnalité du langage, il a sa place.

Mon conseil serait de se concentrer sur votre langage principal, de comprendre tous ses castings et les meilleures pratiques qui y sont associées. Cela devrait guider vos excursions dans d'autres langages.

La documentation pertinente sur C# se trouve ici.

Il y a un excellent résumé des options en C++ dans une question précédente sur SO ici.

9voto

sbi Points 100828

Je parle surtout de C++ ici, mais la plupart de cela s'applique probablement aussi à Java et C# :

Le C++ est un langage à typage statique. Il y a quelques tolérances que le langage permet à ce sujet (fonctions virtuelles, conversions implicites), mais fondamentalement le compilateur connaît le type de chaque objet au moment de la compilation. La raison d'utiliser un tel langage est que les erreurs peuvent être détectées au moment de la compilation. Si le compilateur connaît les types de a et b, alors il vous attrapera au moment de la compilation lorsque vous faites a=ba est un nombre complexe et b est une chaîne de caractères.

Chaque fois que vous effectuez une conversion explicite, vous dites au compilateur de se taire, car vous pensez que vous savez mieux. En cas d'erreur, vous vous en rendrez généralement compte au moment de l'exécution. Et le problème de le découvrir au moment de l'exécution, c'est que cela peut être chez un client.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X