42

What exactly (under the hood) do the += and -= operators do?

Or are they implicit in that they are defined per type?

I've used them extensively, it's a very simple feature of the syntax, but I've never thought about the depths of how it works.

What Brought About the Question

I can concatenate a string value like so:

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

All fine. But why doesn't this work with collections?

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

You may say 'well you're attempting to append a different type, you can't append a string to a type that isn't string'. But the following doesn't work either:

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

Ok, maybe it doesn't work with collections, but is the following not a (kind of) collection of event handlers?

myButton.Click += myButton_Click;

I'm obviously lacking an in-depth understanding of how these operators work.

Please note: I'm not trying to build the collection myCol in this way, in a real project. I'm merely curious about the workings of this operator, it's hypothetical.

user1306322
  • 8,561
  • 18
  • 61
  • 122
  • http://stackoverflow.com/a/6587134/4767498 – M.kazem Akhgary Nov 09 '16 at 09:31
  • 6
    It may be worth noting that "what is addition" is actually quite a tricky little concept in mathematics. It takes a lot of very carefully constructed work to define it, even though the concept is intuitive to many. A similar corollary arises in computer language design. While defining what `+=` does is easy, the in depth understand of *why* we wrote it to work that way is a bit more time consuming. – Cort Ammon Nov 09 '16 at 14:58
  • I mean, really, what should happen with `"10" + "20"` - `"1020"`? Or `"30"`? Who is to tell someone that there's a right or wrong way to answer that? – corsiKa Nov 09 '16 at 17:05
  • 2
    The fundamental problem here is that `+` is used to mean normal numeric addition, string concatenation, and sequencing of multicast delegates, and all three of these operations are only tangentially related to each other. It's a bit of an abuse of the intuition we all have about addition, and this leads to confusion. Your best bet is to think of `+` and `+=` as being several different things that have the same syntax for historical reasons. – Eric Lippert Nov 09 '16 at 19:32
  • 1
    @corsiKa `"10" + "20"` is `"1020"` as they are strings. `10 + 20` is `30` as they are numbers. isn't that obvious (in a staticly typed language)? – Sharky Nov 10 '16 at 07:59
  • @Sharky Being statically typed doesn't imply that the result of `+` on strings should not take into account their numeric values. It's just a matter of the actual implementation. The return type is always string, be it "1020" or "30". – IS4 Nov 16 '16 at 12:16
  • @IllidanS4 Ah, yes i understand now, thanks! `+` is the mathematical sign of addition. So, yes, even if it's about string `Who is to tell someone that there's a right or wrong way to answer that?` thanks for clearing that to me. i can see corsiKa's point now and of course i agree. – Sharky Nov 16 '16 at 12:38

4 Answers4

41

The += operator is implicitly defined like this: a += b turns into a = a + b;, same with the -= operator.
(caveat: as Jeppe pointed out, if a is an expression, it is only evaluated once if you use a+=b, but twice with a=a+b)

You cannot overload the += and -= operator separately. Any type that supports the + operator also supports +=. You can add support for += and -=to your own types by overloading + and -.

There is however one exception hard coded into c#, which you have discovered:
Events have a += and -= operator, which adds and removes an event handler to the list of subscribed event handlers. Despite this, they do not support the + and - operators.
This is not something you can do for your own classes with regular operator overloading.

Community
  • 1
  • 1
HugoRune
  • 13,157
  • 7
  • 69
  • 144
  • For Delegates, `+` is simply Delegate.Combine, isn't it? – Georg Nov 09 '16 at 09:37
  • For variables (and even properties) of delegate type, `a += expr;` is just like `a = a + expr;`. However, ___events___ are something else. For an event `e`, the syntax `e += expr;` is a call to the `add` accessor of the event, with `expr` passed as the argument. (However, the `add` accessor is often implemented by doing something related to ordinary `+=` on a backing variable of delegate type, but it could be any "method"). – Jeppe Stig Nielsen Nov 09 '16 at 14:14
  • 21
    The usual thing to be aware of, is that the left-hand side of `+=` is evaluated only once. For that reason `CallMethod().Member += 10;` is not quite the same as `CallMethod().Member = CallMethod().Member + 10;` because with the former the `CallMethod` is invoked only once, whereas with the latter it is invoked twice. – Jeppe Stig Nielsen Nov 09 '16 at 14:19
  • Why do events have += hard-coded? It seems like the general rule for overloading + would work for them, unless I'm missing something. – Casey Nov 09 '16 at 14:53
  • If `a += b` turns into `a = a + b` then how come the operator is not `=+`? Do you know if there's a reason for this? –  Nov 09 '16 at 15:11
  • 1
    @SamC because the adding/combining (+) happens first, before the assignment (=), hence the order of += – kwah Nov 09 '16 at 15:29
  • 3
    @SamC I think it would also be ambiguous between the =+ operator and using the assignment operator followed by the unary + operator. How would the compiler know if "a =+ b" means "a = a + b" or "a = (+b)"? – George T Nov 09 '16 at 15:46
  • 1
    @GeorgeT That ambiguity would be resolved by [maximal munch](https://en.wikipedia.org/wiki/Maximal_munch), which is almost certainly the general rule in C# (languages that _don't_ do maximal-munch tokenization are very unusual, and I can't think of one off the top of my head). This is purely historical - see reply to SamC below. – zwol Nov 09 '16 at 16:40
  • 1
    @SamC Historical reasons. In the very first iterations of the language now known as C, the shorthand _was_ `a =+ b`, and the same for all the other op-and-assign operators. They were changed to `+=` between [Unix V6](https://en.wikipedia.org/wiki/Version_6_Unix) and [Unix V7](https://en.wikipedia.org/wiki/Version_7_Unix) for readability reasons: given `a=-b`, did the programmer _mean_ to write `a =- b` or `a = -b`? The V6 compiler would take the former interpretation, but Kernighan and Ritchie evidently felt that the second was likely to have been meant. C# borrowed all this from C verbatim. – zwol Nov 09 '16 at 16:46
  • so there is no in-place increment function? it is just a shorthand for `a = a + b`? – njzk2 Nov 09 '16 at 19:15
  • @njzk2 not quite, see Jeppe's comment above. The left-hand side is only evaluated once. – Nick Coad Nov 09 '16 at 21:59
  • 1
    @zwol: Swift doesn't use maximal-munch for operators. Any sequence of operator characters forms an operator, possibly an undefined one. Space on both sides or on neither side = binary operator, space on the left side only = prefix, right side only = suffix. In a =+ b, "=+" is a binary operator. And then you find its not defined. Instead you can write a = +b which is a binary "=" and a prefix "+". – gnasher729 Nov 09 '16 at 22:34
  • 1
    @gnasher729 If the "any sequence of operator characters" is always taken to be the longest possible, then it _is_ using maximal munch. It would not be maximal munch if, for instance, given `a+=b`, on discovering that there is no `=+` operator in scope, the parser backed up and tried `a = + b` instead. – zwol Nov 09 '16 at 22:38
28

As the other answer say, the + operator is not defined for List<>. You can check it trying to overload it, the compiler would throw this error One of the parameters of a binary operator must be the containing type.

But as an experiment, you can define your own class inheriting List<string> and define the + operator. Something like this:

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

Then you can do this without problems:

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

Edit

Per @EugeneRyabtsev comment, my implementation of the + operator would lead to an unexpected behavior. So it should be more something like this:

public static StringList operator +(StringList lhs, StringList rhs)
{
      StringList newList=new StringList();
      newList.AddRange(lhs.ToArray<string>());
      newList.AddRange(rhs.ToArray<string>());
      return newList;
}
Pikoh
  • 7,582
  • 28
  • 53
  • 2
    That is a dangerous experiment in the sense that if you get a habit of doing things like that in production, next thing some other guy does is to write `listString3 = listString1 + listString2;` and guess what happens. – Eugene Ryabtsev Nov 09 '16 at 11:09
  • Well, first of all as I say it's just an experiment to understand what + operator would do in a List. But, what would happen it someone does `listString3 = listString1 + listString2;`? I think it would work as expected... – Pikoh Nov 09 '16 at 11:12
  • 6
    Probably against the expectations of the author, `listString1` will be changed, and changed again with any subsequent modification to `listString3`, because they will be the same list (List<> is a class). Could not be sure that this would be expected by the next guy, even if it is expected by you. – Eugene Ryabtsev Nov 09 '16 at 11:15
  • 1
    I see. You are right, that could happen, but that's not a problem with the experiment, but of my implementation. I will edit to solve it – Pikoh Nov 09 '16 at 11:20
  • And then, before you know it, the next guy does something to listString and expects it to survive the `+=` - like adding listString to a collection, doing `listString.Capacity = LOTS;` before all those subsequent `+=`s etc. Dangerous. – Eugene Ryabtsev Nov 09 '16 at 11:33
  • Well, an experiment is that, an experiment. It's obviously not meant for production. I see your point, really. In fact,all those concerns you have may be the ones that led the .net development team not to include those operators in the first time. Experiments, if you understand that are not for production and explains something, are not dangerous. They can be a mean to understand anything :) – Pikoh Nov 09 '16 at 11:49
  • 3
    Welcome to the reason why `+=` is a better primitive than `+`: Writing `+` in terms of `+=` costs nothing, writing `+=` in terms of `+` requires duplicating the lhs needlessly. Sucks to be C#. – Yakk - Adam Nevraumont Nov 09 '16 at 15:28
  • That actually made me think about something, the + operator gets very messy when you are dealing with mutable objects. – user1837841 Nov 15 '16 at 21:11
15

The short answer is that operators in C# have to be overloaded for a given type. This also holds for +=. string contains an overload of this operator, but List<> has not. Therefore, using the += operator for lists is not possible. For delegates, the += operator is also overloaded, which is why you can use += on event handlers.

The slightly longer answer is that you are free to overload the operator yourself. However, you have to overload it for your own types, so creating an overload for List<T> is not possible, while you in fact can do this for your own class that for instance inherits from List<T>.

Technically, you do not really overload the +=-operator, but the + operator. The += operator is then inferred by combining the + operator with an assignment. For this to work, the + operator should be overloaded in such a way that the result type matches the type of the first argument, otherwise the C#-compiler is going to throw an error message when you try to use +=.

Georg
  • 5,626
  • 1
  • 23
  • 44
  • Thanks, so this is pretty conclusive then. The framework/language knows of no 'standard' way of addition/appending without a type-specific overload? Interesting. –  Nov 09 '16 at 09:30
  • 4
    @JᴀʏMᴇᴇ there is not generic concept of addition / appending in general – fixagon Nov 09 '16 at 09:31
  • Just to say if you open the String class in reference code, the operator + is not overloaded. – mybirthname Nov 09 '16 at 09:36
  • 1
    @mybirthname You will not find an explicit overload of the `+`-operator for strings, true and you won't find an explicit overload for `int`, `double`, etc. either. This is basically because these operator overloads are directly supported by the C#-compiler, much like the `+`-operator for delegates. Interestingly, `decimal` is the only standard numeric BCL class that has an explicit overload for the `+` operator. – Georg Nov 09 '16 at 09:41
4

The correct implementation is actually quite a bit more complex than one would think. First of all, it is not sufficient to say that a += b is exactly the same as a = a+b. It has the same semantics in the simplest of cases, but it is not a simple text substitution.

First, if the expression on the left is more complex than a simple variable, it is only evaluated once. So M().a += b is not the same as M().a = M().a + b, as that would be assigning the value on a completely different object than it was taken from, or would cause the method's side effects to happen twice.

If M() returns a reference type, the compound assignment operator can be thought of as var obj = M(); obj.a = obj.a+b; (but still being an expression). However, if obj was of a value type, this simplification would also not work, in case the method returned a reference (new in C# 7) or if it actually was an array element, or something returned from an indexer etc., then the operator ensures that it doesn't make more copies than needed to modify the object, and it will be applied to a correct place, with no additional side effects.

Event assignment is an entirely different beast, though. Outside the class scope, += results in calling the add accessor, and -= results in calling the remove accessor of the event. If these accessors aren't user-implemented, event assignment might result in calling Delegate.Combine and Delegate.Remove on the internal delegate object inside the class scope. That's also why you cannot simply get the event object outside the class, because it is not public. +=/-= is also not an expression in this case.

I recommend reading Compound Assignment by Eric Lippert. It describes this in much more detail.

IS4
  • 11,945
  • 2
  • 47
  • 86
  • The expression `M().a += b;` would be illegal (compile-time error) is the declared return type of `M` was a value-type (`struct`) with a member `a`. The current implementation of the compiler uses the message _error CS1612: Cannot modify the return value of 'YourType.M()' because it is not a variable_ I think you could be more clear about that. – Jeppe Stig Nielsen Nov 09 '16 at 14:29
  • @JeppeStigNielsen Yes, I am mentioning it supposedly works only for `ref`-returning methods. – IS4 Nov 09 '16 at 20:17