Pendant que j'apprenais le C #, j'ai trouvé que le C # prend en charge la surcharge de l'opérateur. J'ai un problème avec un bon exemple qui:
- Faire sens (ex. Ajouter une classe nommée moutons et vaches)
- N'est pas un exemple de concaténation de deux chaînes
Les exemples de la bibliothèque de classes de base sont les bienvenus.
==
pour faire de la multiplication, cela a du sens pour moi mais peut ne pas avoir de sens pour les autres! S'agit-il de la légitimité de quels langages de programmation d'installation ou parlons-nous de «meilleures pratiques de codage»?Réponses:
Les exemples évidents de surcharge d'opérateur appropriée sont toutes les classes qui se comportent de la même manière que les nombres fonctionnent. Ainsi, les classes BigInt (comme le suggère Jalayn ), les nombres complexes ou les classes matricielles (comme le suggère Superbest ) ont toutes les mêmes opérations que les nombres ordinaires si bien mappées très bien sur les opérateurs mathématiques, tandis que les opérations temporelles (comme suggéré par svick ) mappent bien sur un sous-ensemble de ces opérations.
Un peu plus abstraitement, les opérateurs pourraient être utilisés lors de l'exécution d' opérations de type ensemble , donc
operator+
pourraient être une union ,operator-
un complément, etc. Cela commence à étirer le paradigme, surtout si vous utilisez l'opérateur d'addition ou de multiplication pour une opération qui n'est t commutative , comme on pourrait s'y attendre.C # lui-même a un excellent exemple de surcharge d'opérateur non numérique . Il utilise
+=
et-=
pour ajouter et soustraire des délégués , c'est-à-dire les enregistrer et les désenregistrer. Cela fonctionne bien car les opérateurs+=
et-=
fonctionnent comme vous vous en doutez, ce qui donne un code beaucoup plus concis.Pour le puriste, l'un des problèmes avec l'
+
opérateur de chaîne est qu'il n'est pas commutatif."a"+"b"
n'est pas le même que"b"+"a"
. Nous comprenons cette exception pour les chaînes car elle est si courante, mais comment savoir si l'utilisationoperator+
sur d'autres types sera commutative ou non? La plupart des gens penseront que c'est le cas, à moins que l'objet ne soit semblable à une chaîne , mais vous ne savez jamais vraiment ce que les gens supposeront.Comme pour les cordes, les faiblesses des matrices sont également assez bien connues. Il est évident que
Matrix operator* (double, Matrix)
c'est une multiplication scalaire, alors que ceMatrix operator* (Matrix, Matrix)
serait une multiplication matricielle (c'est-à-dire une matrice de multiplications de produits scalaires) par exemple.De même, l'utilisation d'opérateurs avec des délégués est tellement loin des mathématiques qu'il est peu probable que vous fassiez ces erreurs.
Soit dit en passant, lors de la conférence ACCU 2011 , Roger Orr et Steve Love ont présenté une session sur Certains objets sont plus égaux que d'autres - un regard sur les nombreuses significations de l'égalité, de la valeur et de l'identité . Leurs diapositives sont téléchargeables , tout comme l' annexe de Richard Harris sur l'égalité en virgule flottante . Résumé: Soyez très prudent avec
operator==
, voici des dragons!La surcharge des opérateurs est une technique sémantique très puissante, mais elle est facile à sur-utiliser. Idéalement, vous ne devriez l'utiliser que dans des situations où il est très clair, d'après le contexte, quel est l'effet d'un opérateur surchargé. À bien des égards,
a.union(b)
est plus clair quea+b
, eta*b
est beaucoup plus obscur quea.cartesianProduct(b)
, d'autant plus que le résultat d'un produit cartésien serait unSetLike<Tuple<T,T>>
plutôt qu'unSetLike<T>
.Les vrais problèmes de surcharge d'opérateur surviennent lorsqu'un programmeur suppose qu'une classe se comportera d'une manière, mais qu'elle se comportera en fait d'une autre. Ce genre de choc sémantique est ce que je suggère qu'il est important d'éviter.
la source
d1 + d2
pour deux délégués du même type.Je suis surpris que personne n'ait mentionné l'un des cas les plus intéressants de BCL:
DateTime
etTimeSpan
. Vous pouvez:TimeSpan
s pour obtenir un autreTimeSpan
TimeSpan
pour obtenir une négationTimeSpan
DateTime
s pour obtenir unTimeSpan
TimeSpan
d'unDateTime
pour obtenir un autreDateTime
Une autre série d'opérateurs qui pourrait avoir un sens sur un grand nombre de types sont
<
,>
,<=
,>=
. Dans la BCL, par exemple, lesVersion
implémente.la source
Le premier exemple qui me vient à l'esprit est l'implémentation de BigInteger , qui vous permet de travailler avec de grands entiers signés. Consultez le lien MSDN pour voir combien d'opérateurs ont été surchargés (c'est-à-dire qu'il y a une grande liste, et je n'ai pas vérifié si tous les opérateurs ont été surchargés, mais cela semble certainement le cas)
De plus, comme je fais aussi Java et Java ne permet pas de surcharger les opérateurs, il est incroyablement plus doux d'écrire
Que, en Java:
la source
Je suis content d'avoir vu cela parce que je plaisante avec Irony et qu'il a une GRANDE utilisation de la surcharge de l'opérateur. Voici un exemple de ce qu'il peut faire.
Irony est donc un "Kit de mise en œuvre du langage .NET" et est un générateur d'analyseur (générant un analyseur LALR). Au lieu d'avoir à apprendre une nouvelle syntaxe / langue comme les générateurs d'analyseurs tels que yacc / lex, vous écrivez la grammaire en C # avec la surcharge de l'opérateur. Voici une simple grammaire BNF
C'est donc une simple petite grammaire (veuillez l'excuser s'il y a des incohérences car j'apprends juste le BNF et je construis des grammaires). Voyons maintenant le C #:
Comme vous pouvez le voir, avec la surcharge de l'opérateur, l'écriture de la grammaire en C # équivaut presque exactement à la grammaire en BNF. Pour moi, non seulement cela a du sens, mais c'est une excellente utilisation de la surcharge de l'opérateur.
la source
L'exemple clé est operator == / operator! =.
Si vous souhaitez comparer facilement deux objets avec des valeurs de données au lieu de par référence, vous voudrez surcharger .Equals (et.GetHashCode!), Et vous voudrez peut-être également utiliser les opérateurs! = Et == pour des raisons de cohérence.
Je n'ai cependant jamais vu de surcharges sauvages d'autres opérateurs en C # (j'imagine qu'il y a des cas extrêmes où cela pourrait être utile).
la source
Cet exemple de MSDN montre comment implémenter des nombres complexes et les faire utiliser l'opérateur normal +.
Un autre exemple montre comment le faire pour l'ajout de matrice, et explique également comment ne pas l'utiliser pour ajouter une voiture à un garage (lire le lien).
la source
Une bonne utilisation de la surcharge peut être rare, mais cela arrive.
surcharge opérateur == et opérateur! = montrent deux écoles de pensée: celles qui disent que cela facilite les choses, et celles qui ne disent pas cela empêchent de comparer les adresses (c'est-à-dire que je pointe exactement vers le même endroit en mémoire, pas seulement une copie de la même chose) objet).
Je trouve que les surcharges d'opérateurs de cast sont pratiques dans des situations spécifiques. Par exemple, j'ai dû sérialiser / désérialiser en XML un booléen représenté par 0 ou 1. L'opérateur de cast droit (implicite ou explicite, j'oublie) de booléen en int et en arrière a fait l'affaire.
la source
object.ReferenceEquals()
.==
par le cast:(object)foo == (object)bar
compare toujours les références. Mais je préféreraisReferenceEquals()
, comme @ dan04 le mentionne parce que c'est plus clair ce qu'il fait.Ils ne sont pas dans la catégorie des choses auxquelles les gens pensent généralement lorsqu'ils pensent à la surcharge des opérateurs, mais je pense que l'un des opérateurs les plus importants pour pouvoir surcharger est l' opérateur de conversion .
Les opérateurs de conversion sont particulièrement utiles pour les types de valeur qui peuvent "supprimer le sucre" en un type numérique, ou peuvent agir comme un type numérique dans certains contextes. Par exemple, vous pouvez définir un
Id
type spécial qui représente un certain identificateur, et vous pouvez fournir une conversion implicite àint
afin que vous puissiez passer unId
à une méthode qui prend unint
, mais une conversion explicite deint
àId
afin que personne ne puisse passer unint
à un méthode qui prend unId
sans le lancer au préalable.À titre d'exemple en dehors de C #, le langage Python inclut de nombreux comportements spéciaux qui sont implémentés en tant qu'opérateurs surchargeables. Ceux-ci incluent l'
in
opérateur pour tester l'appartenance, l'()
opérateur pour appeler un objet comme s'il s'agissait d'une fonction et l'len
opérateur pour déterminer la longueur ou la taille d'un objet.Et puis vous avez des langages comme Haskell, Scala, et de nombreux autres langages fonctionnels, où des noms comme ne
+
sont que des fonctions ordinaires, et pas du tout des opérateurs (et il y a un support de langage pour utiliser des fonctions en position d'infixe).la source
La structure ponctuelle dans l' espace de noms System.Drawing utilise la surcharge pour comparer deux emplacements différents à l'aide de la surcharge de l'opérateur.
Comme vous pouvez le voir, il est beaucoup plus facile de comparer les coordonnées X et Y de deux emplacements en utilisant la surcharge.
la source
Si vous connaissez le vecteur mathématique, vous pourriez voir une utilité pour surcharger l'
+
opérateur. Vous pouvez ajouter un vecteura=[1,3]
avecb=[2,-1]
et obtenirc=[3,2]
.La surcharge des égaux (==) peut également être utile (même s'il est probablement préférable d'implémenter une
equals()
méthode). Pour continuer les exemples vectoriels:la source
Imaginez un morceau de code pour dessiner sur un formulaire
Un autre exemple courant est lorsqu'une structure est utilisée pour contenir des informations de position sous la forme d'un vecteur.
à utiliser plus tard comme
la source
operator+
doit pas être surchargé (vous pouvez implémenter un point en termes de vecteur, mais vous ne devriez pas pouvoir ajouter deux points)p1+((p2-p1)+(p3-p1)+(p4-p1))/4
, mais cela semble un peu maladroite.