web-dev-qa-db-fra.com

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

Que font exactement (sous le capot) le += et -= les opérateurs font?

Ou sont-ils implicites dans la mesure où 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é à la profondeur de son fonctionnement.

Qu'est-ce qui a amené la question

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

var myString = "hello ";
myString += "world";

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

var myCol = new List<string>();
myCol += "hi";

Vous pouvez dire "eh bien, vous essayez d'ajouter un type différent, vous ne pouvez pas ajouter 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<string>();
myCol += new List<string>() { "hi" };

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

myButton.Click += myButton_Click;

Je manque manifestement d'une compréhension approfondie du fonctionnement de ces opérateurs.

Remarque: je n'essaie pas de construire la collection myCol de cette manière, dans un vrai projet. Je suis simplement curieux de connaître le fonctionnement de cet opérateur, c'est hypothétique.

42
user1017882

Le += L'opérateur est implicitement défini comme ceci: a += b se transforme en a = a + b;, idem avec le -= opérateur.
(mise en garde: comme Jeppe l'a souligné, si a est une expression, elle n'est évaluée qu'une seule fois si vous utilisez a+=b, mais deux fois avec a=a+b)

Vous ne pouvez pas surcharger le += et -= opérateur séparément. Tout type prenant en charge le + L'opérateur prend également en charge +=. Vous pouvez ajouter la prise en charge de += et -=à vos propres types par surcharge + et - .

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

41
HugoRune

Comme le dit l'autre réponse, le + L'opérateur n'est pas défini pour List<>. Vous pouvez le vérifier en essayant de le surcharger, le compilateur lèverait 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 en héritant List<string> et définissez le + opérateur. Quelque chose comme ça:

class StringList : List<string>
{
    public static StringList operator +(StringList lhs, StringList rhs)
    {
        lhs.AddRange(rhs.ToArray<string>());
        return lhs;
    }
}

Ensuite, vous pouvez le faire sans problème:

StringList listString = new StringList() { "a", "b", "c" };
StringList listString2 = new StringList() { "d", "e", "f" };
listString += listString2;

Modifier

Selon le commentaire @EugeneRyabtsev, mon implémentation du + L'opérateur entraînerait un comportement inattendu. Donc ça devrait être plus quelque chose comme ça:

public static StringList operator +(StringList lhs, StringList rhs)
{
      StringList newList=new StringList();
      newList.AddRange(lhs.ToArray<string>());
      newList.AddRange(rhs.ToArray<string>());
      return newList;
}
28
Pikoh

La réponse courte est que les opérateurs en C # doivent être surchargés pour un type donné. Cela vaut également pour +=. string contient une surcharge de cet opérateur, mais pas List<>. Par conséquent, l'utilisation de l'opérateur += Pour les listes n'est pas possible. Pour les délégués, l'opérateur += Est également surchargé, c'est 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, donc créer une surcharge pour List<T> N'est pas possible, alors que vous pouvez en fait le faire pour votre propre classe qui hérite par exemple de List<T>.

Techniquement, vous ne surchargez pas vraiment l'opérateur += -, mais l'opérateur +. L'opérateur += Est ensuite déduit en combinant l'opérateur + Avec une affectation. Pour que cela fonctionne, l'opérateur + Doit être surchargé de telle sorte que le type de résultat corresponde au type du premier argument, sinon le compilateur C # lancera un message d'erreur lorsque vous essayez d'utiliser +=.

15
Georg

La mise en œuvre correcte est en fait un peu plus complexe qu'on ne le pense. 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 le plus simple des cas, mais ce n'est pas une simple substitution de texte.

Premièrement, si l'expression de gauche est plus complexe qu'une simple variable, elle n'est évaluée qu'une seule fois. Ainsi, M().a += b n'est pas la même chose que M().a = M().a + b, car cela attribuerait la valeur à un objet complètement différent de celui d'où elle proviendrait, ou provoquerait deux effets secondaires de la méthode.

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; (mais étant toujours une expression). Cependant, si obj était d'un type valeur, cette simplification ne fonctionnerait pas non plus, dans le cas où la méthode renvoyait une référence (nouveau en C # 7) ou s'il s'agissait en fait d'un élément de tableau, ou quelque chose retourné par un indexeur etc., alors l'opérateur s'assure qu'il ne fait pas plus de copies que nécessaire pour modifier l'objet, et il sera appliqué à un endroit correct, sans effets secondaires supplémentaires.

L'affectation des événements est une bête entièrement différente. En dehors de la portée de la classe , += Entraîne l'appel de l'accesseur add et -= entraîne l'appel de l'accesseur remove 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'objet délégué interne à l'intérieur de l'étendue de la classe . C'est aussi pourquoi vous ne pouvez pas simplement obtenir l'objet é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 la lecture Assignation composée par Eric Lippert. Il décrit cela plus en détail.

4
IllidanS4