user1017882
user1017882

Reputation:

How EXACTLY can += and -= operators be interpreted?

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.

Upvotes: 42

Views: 7085

Answers (4)

HugoRune
HugoRune

Reputation: 13809

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.

Upvotes: 41

IS4
IS4

Reputation: 13207

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.

Upvotes: 4

Pikoh
Pikoh

Reputation: 7703

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;
}

Upvotes: 28

Georg
Georg

Reputation: 5781

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 +=.

Upvotes: 15

Related Questions