Comment EXACTEMENT peut-on interpréter les opérateurs + = et – =?

Que font exactement les opérateurs += et -= ?

Ou sont-ils implicites en ce sens qu’ils sont définis par type?

Je les ai largement utilisés, c’est une fonctionnalité très simple de la syntaxe, mais je n’ai jamais pensé aux profondeurs de son fonctionnement.

Ce qui a amené la question

Je peux concaténer une valeur de chaîne comme suit:

 var mySsortingng = "hello "; mySsortingng += "world"; 

Tout va bien. Mais pourquoi cela ne fonctionne-t-il pas avec les collections?

 var myCol = new List(); myCol += "hi"; 

Vous pouvez dire “vous essayez d’append un type différent, vous ne pouvez pas append une chaîne à un type qui n’est pas une chaîne”. Mais ce qui suit ne fonctionne pas non plus:

 var myCol = new List(); myCol += new List() { "hi" }; 

Ok, peut-être que cela ne fonctionne pas avec les collections, mais est-ce que ce qui suit n’est pas une (sorte de) collection de gestionnaires d’événements?

 myButton.Click += myButton_Click; 

Je manque évidemment d’une compréhension approfondie de la façon dont ces opérateurs fonctionnent.

S’il vous plaît noter: je myCol pas de construire la collection myCol de cette façon, dans un vrai projet. Je suis simplement curieux du fonctionnement de cet opérateur, c’est hypothétique.

L’opérateur += est implicitement défini comme ceci: a += b se transforme en a = a + b; , même avec l’opérateur -= .
(mise en garde: comme Jeppe l’a fait remarquer, si a est une expression, elle n’est évaluée qu’une fois si vous utilisez a+=b , mais deux fois avec a=a+b )

Vous ne pouvez pas surcharger l’opérateur += et -= séparément. Tout type qui prend en charge l’opérateur += prend également en charge += . Vous pouvez append un support pour += et -= à vos propres types en surchargeant + et - .

Il y a cependant une exception codée en dur dans c #, que vous avez découverte:
Les événements ont un opérateur += et -= , qui ajoute et supprime un gestionnaire d’événements à la liste des gestionnaires d’événements abonnés. Malgré cela, ils ne supportent pas les opérateurs + et - .
Ce n’est pas quelque chose que vous pouvez faire pour vos propres classes avec une surcharge régulière de l’opérateur.

Comme le dit l’autre réponse, l’opérateur + n’est pas défini pour List<> . Vous pouvez le vérifier en essayant de le surcharger, le compilateur jetterait cette erreur One of the parameters of a binary operator must be the containing type .

Mais, à titre expérimental, vous pouvez définir votre propre classe héritant de List et définir l’opérateur + . Quelque chose comme ça:

 class SsortingngList : List { public static SsortingngList operator +(SsortingngList lhs, SsortingngList rhs) { lhs.AddRange(rhs.ToArray()); return lhs; } } 

Ensuite, vous pouvez le faire sans problèmes:

 SsortingngList listSsortingng = new SsortingngList() { "a", "b", "c" }; SsortingngList listSsortingng2 = new SsortingngList() { "d", "e", "f" }; listSsortingng += listSsortingng2; 

modifier

Par commentaire @EugeneRyabtsev, mon implémentation de l’opérateur + entraînerait un comportement inattendu. Donc ça devrait être plus comme ça:

 public static SsortingngList operator +(SsortingngList lhs, SsortingngList rhs) { SsortingngList newList=new SsortingngList(); newList.AddRange(lhs.ToArray()); newList.AddRange(rhs.ToArray()); return newList; } 

La réponse courte est que les opérateurs en C # doivent être surchargés pour un type donné. Cela vaut également pour += . ssortingng contient une surcharge de cet opérateur, mais non List<> . Par conséquent, utiliser l’opérateur += pour les listes n’est pas possible. Pour les delegates, l’opérateur += est également surchargé, ce qui explique pourquoi vous pouvez utiliser += sur les gestionnaires d’événements.

La réponse un peu plus longue est que vous êtes libre de surcharger l’opérateur vous-même. Cependant, vous devez le surcharger pour vos propres types, il n’est donc pas possible de créer une surcharge pour List , alors que vous pouvez le faire pour votre propre classe qui, par exemple, hérite de List .

Techniquement, vous ne surchargez pas vraiment l’opérateur += , mais l’opérateur + . L’opérateur += est alors déduit en combinant l’opérateur + avec une affectation. Pour que cela fonctionne, l’opérateur + doit être surchargé de sorte que le type de résultat corresponde au type du premier argument, sinon le compilateur C # va envoyer un message d’erreur lorsque vous essayez d’utiliser += .

La mise en œuvre correcte est en fait un peu plus complexe qu’on pourrait le penser. Tout d’abord, il ne suffit pas de dire que a += b est exactement le même que a = a+b . Il a la même sémantique dans les cas les plus simples, mais ce n’est pas une simple substitution de texte.

Tout d’abord, si l’expression à gauche est plus complexe qu’une variable simple, elle n’est évaluée qu’une seule fois. Donc M().a += b n’est pas la même chose que M().a = M().a + b , car cela atsortingbuerait la valeur à un object complètement différent les effets secondaires de la méthode se produiront deux fois.

Si M() renvoie un type de référence, l’opérateur d’affectation composé peut être considéré comme var obj = M(); obj.a = obj.a+b; var obj = M(); obj.a = obj.a+b; (mais étant toujours une expression). Cependant, si obj était de type valeur, cette simplification ne fonctionnerait pas non plus, si la méthode renvoyait une référence (nouvelle dans C # 7) ou si c’était un élément de tableau, ou quelque chose renvoyé par un indexeur, etc. L’opérateur s’assure qu’il ne fait pas plus de copies que nécessaire pour modifier l’object, et qu’il sera appliqué à un endroit correct, sans effets secondaires supplémentaires.

L’affectation d’événement est une bête complètement différente, cependant. En dehors de la scope de la classe , += entraîne l’appel de l’accesseur add et -= entraîne l’appel de l’ absorbeur de suppression de l’événement. Si ces accesseurs ne sont pas implémentés par l’utilisateur, l’affectation d’événement peut entraîner l’appel de Delegate.Combine et Delegate.Remove sur l’object délégué interne dans l’étendue de la classe. C’est aussi pourquoi vous ne pouvez pas simplement obtenir l’object événement en dehors de la classe, car il n’est pas public. += / -= n’est pas non plus une expression dans ce cas.

Je recommande de lire Compound Assignment par Eric Lippert. Il décrit cela beaucoup plus en détail.