une caractéristique quelque peu peu connue de C # est la possibilité de créer implicite ou explicite conversions de type défini par l'utilisateur . J'ai écrit C # Code pendant 6 ans maintenant, et je ne l'ai jamais utilisé. Donc, j'ai peur d'avoir manqué de bonnes opportunités. P>
Quelles sont les utilisations légitimes et bonnes des conversions définies par l'utilisateur? Avez-vous des exemples où ils sont meilleurs que de définir une méthode personnalisée? P>
- p>
s'avère, Microsoft dispose de Directives de conception sur les conversions, la le plus pertinent est: p>
ne fournissez pas d'opérateur de conversion si cette conversion n'est pas clairement attendu par les utilisateurs finaux. P> blockQuote>
Mais quand une conversion est-elle "attendue"? En dehors des classes de nombres de jouets, je ne peux pas comprendre un cas d'utilisation du monde réel. P>
Voici un résumé des exemples fournis dans les réponses: P>
- Radians / degrés / Double Li>
- Polar / Point2D Li>
- Kelvin / Farenheit / Celsius Li> ul>
Le motif semble être: les conversions implicites sont principalement (seulement?) utiles lors de la définition de types numériques / de valeur, la conversion étant définie par une formule. En rétrospectivement, c'est une sorte d'évidence. Néanmoins, je me demande si des classes non numériques pouvaient également bénéficier de conversions implicites. P>
6 Réponses :
Dites que vous avez une classe pour un produit (par exemple un jouet) que vous utilisez pour une application Shops:
public static explicit operator string(Product p) { return (p.price * 100).ToString(); //... }
Il y a déjà une méthode Tostring () pour cela. Comment une conversion est-elle meilleure? Cela ne semble pas être un cas d'utilisation convaincante. Je pense que la SP signifie que vous devriez vous abstenir de définir une conversion pour cette classe.
Je ne suis pas un gros fan de la myProduct code> exemple, car on s'attendrait à ce que cela s'attendrait à ce que cela soit manipulé par Tostring. Un meilleur exemple serait si vous étiez à la place des chaînes aux produits.
Je viens d'utiliser la conversion de chaîne comme exemple; Vous pouvez utiliser n'importe quel type de conversion que vous pensez que votre classe pourrait être convertie. Par exemple, pour la classe code> code>, vous pouvez définir un opérateur Explicit int code> qui retournerait le prix.
Généralement, si deux choses sont logiquement convertibles. Je les utilise dans de telles situations pour fournir plus de code fluide. Je les utilise aussi parfois pour contourner les fonctionnalités de la langue qui ne fonctionnent pas vraiment comme je l'attends.
Voici un exemple très simple et artificiel qui illustre la dernière idée qui ressemble à quelque chose que j'ai utilisé dans la production. .. p>
Oui, il semble un peu conçu ... et ici, la conversion implicite permet également d'écrire "int n = code1 + code2;" ce qui n'a aucun sens. Je pense que simplement écrire "Switch (code1.Id)" est ok.
Vous avez raison. Mais c'est une chance que vous devez prendre parfois. Peu importe ce que vous faites, vous ne pouvez pas vaincre la stupidité.
Vous pouvez utiliser un opérateur de conversion lorsqu'il y a une conversion naturelle et claire vers ou depuis un type différent.
Dites par exemple que vous avez un type de données pour représenter des températures: P>
Temperature a = new Temperature(100, TemperatureScale.Celcius); Temperature b = 373.15; // Kelvin is default
En général, toute unité de mesure est un bon candidat à ce sujet.
Ne devriez-vous pas réellement mettre en œuvre cela comme 3 classes distinctes (Kelvin, Farenheit, Celsius) avec des conversions implicites entre eux et double?
@EldritchConundrum: Cela dépend de ce que vous décidez que l'objet décrit. Je dirais que cela décrit essentiellement un niveau d'énergie, et Kelvin / Farenheit / Celsius ne sont que des façons différentes de la décrire. Comparez comment DateTime code> décrit un point à temps et local / Universal est des moyens différents de la décrire.
Comme mentionné dans les commentaires, les degrés et les rotations sont un bon exemple pour éviter de mélanger des valeurs doubles, en particulier entre API.
J'ai tiré des radians Ces avantages sont vraiment en cours d'utilisation d'une API. Bien que, en interne, votre organisation pourrait décider de coller strictement avec des radians ou em> pour éviter les mélanges, au moins avec ces classes, vous pouvez utiliser le type qui a le plus de sens. Par exemple, des API ou des API d'interface graphique publiquement consommées peuvent utiliser ouais, le code est assez artificiel (et terriblement ambigu) mais ça va! Va juste pour montrer comment il peut aider à réduire les mélanges accidentels. P> alors ils peuvent être vraiment utiles pour la mise en œuvre Autres concepts mathématiques basés sur des angles, tels que En tant que coordonnée Polar: p> enfin, vous pouvez mettre en œuvre une classe Comme je l'ai dit, je veux prendre une autre passe à ces classes et éliminer probablement le Edit: Voici la classe code> et
degrés code > Classes Nous utilisons actuellement et ils sont ici. Regardez-les maintenant (après si longtemps), je veux les nettoyer (surtout les commentaires / documentation) et assurez-vous qu'ils sont correctement testés. Heureusement, j'ai réussi à avoir du temps dans la planification de le faire. En tout cas, utilisez-les à vos risques et périls, je ne peux pas garantir si tous les em> les mathématiques ici sont corrects car je suis sûr que nous n'avons pas vraiment utilisé / testé em > Toutes les fonctionnalités que nous avons écrites. P>
radians h3>
degrés h3>
Quelques échantillons d'utilisation h3>
degrés code> alors que votre usage de mathématiques / trigeurs ou internes lourds peut utiliser
radians code>. Considérant les classes / la fonction d'impression suivantes: p>
point2d code> (ou utiliser le système.windows.point) avec des conversions implicites sur / de
polaire code>: p>
double code > Conversions implicites sur
Radians CODE> Pour éviter les mélanges de cas d'angle et les ambiguïtés du compilateur possibles. C'étaient en fait là avant que nous ayons créé le
static
one_pi code>,
demi_pi code> (et ainsi de suite) et que nous étions convertis de certains multiples du
MATH.PI CODE > Double. P>
polaire code> une démonstration de conversions implicites supplémentaires. Il tire parti de la classe code> radians code> (et donc de ses conversions implicites) et des méthodes d'assistance sur elle et la classe
point2d code>. Je ne l'ai pas inclus ici, mais la classe
polaire code> peut facilement mettre en œuvre des opérateurs interagir avec la classe
point2d code>, mais ceux-ci ne sont pas pertinents pour cette discussion. P> < Pré> xxx pré> p>
Je pense que votre réduction () ne fonctionne pas comme prévu sur des angles négatifs.
Les radians / degrés / Double et Polar / Point2D sont de bons exemples d'utilisation de conversions implicites pour rendre le code plus sûr, donc je marquais cela comme réponse. Je me demande si des classes non numériques peuvent également bénéficier de conversions implicites.
@Eldritchconundrum Je ne serais pas surpris. C'est une ancienne méthode portée d'un vieux système flash ancien que nous avions. Il est destiné à convertir n'importe quel angle en un équivalent compris entre 0 et 2pi (ce qui signifie convertir un angle négatif en un positif équivalent). Mes tests le montrent de fonctionner pour des angles négatifs, pouvez-vous fournir un angle que vous avez utilisé ou que le comportement que vous avez obtenu (qui est destiné)? À Anyre, c'est un exemple d'une méthode que je veux réécrire pour être plus claire de toute façon.
@ElDitchConundrum Certains exemples rapides de non-numériques peuvent être des chaînes de localisation "EN-US" à un objet personnalisé, ou nous avons déjà ajouté aux autres pour convertir couleur code> à un équivalent
solidcolorbrush code> (pour la commodité). C'est certainement l'un de ces outils que Microsoft a ajouté que, sans que strictement i> nécessaire est destiné à rendre le code plus lisible et maintenu (s'il n'est pas abusé!). Je rappelle juste une question à une question utilisant des opérateurs implicites pour désigner les "types compatibles" qui ont été vérifiés: Stackoverflow.com/Questtions/10622694/Mandling-unsupporteDetty pes / ...
Votre utilisation de conversions implicites dans la syntaxe d'initialisation de la liste est un abus intéressant, de la sorte de «utilisation C # comme DSL». Je suis plus incertain des conversions de la couleur à la solidcolorbrush, ils peuvent être mieux partis explicites. Et je pense que votre réduction () sur les valeurs inférieures à -2 * PI retourne toujours un nombre négatif, mais cela ne me dérange pas car je n'ai pas l'intention de l'utiliser :)
@EldritchConundrum vient de faire un test rapide et la méthode de réduction fonctionne correctement pour les valeurs inférieures à -2PI, mais cela va simplement montrer pourquoi je veux réécrire pour être plus compréhensible. Je conviens totalement que les conversions implicites dans l'initialisation de la liste pour des types compatibles sont les abus et ne l'utiliseraient probablement pas moi-même. De même, je conviens que les conversions de la couleur à la solidcolorbrush. Je pense qu'ils sont des cas de ce qui a du sens pour chaque utilisation et chaque entreprise. Pour nos développeurs, les conversions de brosse nous ont effectivement sauvé des maux de tête et ont facilité des choses (d'une manière ou d'une autre).
Laissez-nous Continuez cette discussion en chat
J'aime cet exemple, en particulier parce qu'une structure de rotation peut limiter / modulo ses valeurs comprises entre 0 et 360 degrés (ou 2pi) dans le constructeur, ce qui signifie que vous pouvez augmenter une valeur de rotation infiniment et ne jamais obtenir un débordement.
Je l'utilise pour avoir une conversion transparente de Par exemple: p> DateTime code> à
"yyyymmdd" code> ou à sa valeur
int code> (yyyymmdd).
Intéressant! Cela vous permet de définir orthfrom code> une seule fois, en lui donnant plusieurs types de retour possibles. Normalement, j'écrirais une surcharge courte pour chaque type de retour souhaité, mais votre approche échoue mieux avec le nombre de fonctions pour définir. De plus, il ne perd pas de vérification des erreurs de type-temps compilé.
Il n'y a pas de réponse générale. Je l'utiliserais soigneusement, et seulement si le code conserve le code compréhensible et droit (c'est-à-dire qu'il affiche le comportement attendu).
Pour que je puisse vous donner une réponse basée sur un exemple pratique, si vous le suivez, vous comprendrez Quand utiliser et quand il est préférable de ne pas utiliser les opérateurs de conversion: P>
Je voulais récemment avoir un moyen plus simple de gérer les GUID. Mes objectifs de conception étaient les suivants: Simplifiez la syntaxe et l'initialisation, simplifier les conversions et les assignations variables. P>
Comme vous le savez, si vous devez créer des GUID, l'utilisation est un peu encombrante: p>
Exemple 1: strong> p> hors de la boîte: p> Et si vous pouviez simplement convertir implicitement la chaîne de guidage en chaîne, comme: P> var eg1 = new EasyGuid(); // simple case: new Guid
Guid g = eg1; g.Dump(); // straight-forward conversion
EasyGuid eg2 = null; // null-handling
Guid g2 = eg2; g2.Dump(); // converted to 00000000-0000-0000-0000-000000000000
Guid? g3 = eg2; g3.Dump(); // will be null
Changer le comportement du constructeur par défaut d'un type, à l'aide d'un type d'emballage qui convertit implicitement sur le type emballé ... Oui, cela fonctionne. Mais je ne pense pas que ce soit une bonne idée de cacher les conversions, cela ajoute une complexité ... Sauf si vous utilisez C # en tant que DSL, peut-être.
@Eldritch: Comme je l'ai écrit, je l'utiliserais soigneusement. Et il n'y a pas beaucoup de cas d'utilisation où vous avez vraiment besoin de l'opérateur de conversion. Ici, il n'est pas non plus nécessaire, cela simplifie simplement la syntaxe. Que ce soit utiliser ou non, est une question de préférences personnelles. Parfois, il est bon d'avoir un exemple, de savoir comment on dirait - ce qui vous permet d'estimer si vous voulez aller cette voie ou non.
Spécifiquement pour l'initialisation de la matrice, l'attente en C # est que l'allocation d'une matrice de taille permettra à tous les éléments de la matrice, quelle que soit la valeur par défaut du type - souvent 0 code>. Comme un piège ait un type qui produit une valeur aléatoire pour chaque élément à la place.
@Oskarberggren - a accepté, j'ai changé le code, il attribue maintenant un GUID vide dans l'exemple de la matrice.
Un exemple en termes d'applications pratiques: à mon travail, nous avons créé des codes> distincts code> et radians code> classes pour gérer les angles. Ils ont des conversions implicites sur / des uns des autres et des
double code> et ont Immenselement i> simplifiée notre utilisation des angles et tous sauf les cas éliminés d'utilisation accidentelle à la place des radians (et inversement ) dans diverses fonctions trigonométriques.
Très agréable! C'est comme si vous recréez les unités de mesure de F # avec les conversions définies par l'utilisateur de C #.
J'évite les opérateurs de conversion car ils ne sont pas découverts via Intellisense comme des méthodes et des constructeurs, ce qui facilite la définition d'une conversion.