# Compound Assignment Operators

A compound assignment operator is a combination of a binary operator and the assignment (=) operator. Its syntax is -

x op = y -

where op represents the operator. Note that instead of the rvalue being replaced by the lvalue, the compound operator has the effect of writing -

x = x op y -

using the lvalue as the base for the result of the operation.

Note that I use the words "has the effect." The compiler doesn't literally translate something like x += 5 into x = x + 5. It just works that way logically. In fact, there's a major caveat to using an operation when the lvalue is a method. Let's examine that now: -

```using System;
class CompoundAssignment1App
{
protected int[] elements;
public int[] GetArrayElement()
{
return elements;
}
CompoundAssignment1App()
{
elements = new int[1];
elements[0] = 42;
}
public static void Main()
{
CompoundAssignment1App app = new CompoundAssignment1App();
Console.WriteLine("{0}", app.GetArrayElement()[0]);
app.GetArrayElement()[0] = app.GetArrayElement()[0] + 5;
Console.WriteLine("{0}", app.GetArrayElement()[0]);
}
}
```

Notice in the line in boldface-the call to the CompoundAssignment1App.- GetArrayElements method and subsequent modification of its first element-that I use the assignment syntax of -

x = x op y -

Here's the generated MSIL code: -

```// Inefficient technique of x = x op y.
.method public hidebysig static void Main() il managed
{
.entrypoint
// Code size       79 (0x4f)
.maxstack  4
.locals (class CompoundAssignment1App V_0)
IL_0000:  newobj     instance void CompoundAssignment1App::.ctor()
IL_0005:  stloc.0
IL_0006:  ldstr      "{0}"
IL_000b:  ldloc.0
IL_000c:  call       instance int32[] CompoundAssignment1App::GetArrayElement()
IL_0011:  ldc.i4.0
IL_0012:  ldelema    ['mscorlib']System.Int32
IL_0017:  box        ['mscorlib']System.Int32
IL_001c:  call       void ['mscorlib']System.Console::WriteLine
(class System.String,
class System.Object)
IL_0021:  ldloc.0
IL_0022:  call
instance int32[] CompoundAssignment1App::GetArrayElement()
IL_0027:  ldc.i4.0
IL_0028:  ldloc.0
IL_0029:  call
instance int32[] CompoundAssignment1App::GetArrayElement()
IL_002e:  ldc.i4.0
IL_002f:  ldelem.i4
IL_0030:  ldc.i4.5
IL_0031:  add
IL_0032:  stelem.i4
IL_0033:  ldstr      "{0}"
IL_0038:  ldloc.0
IL_0039:  call
instance int32[] CompoundAssignment1App::GetArrayElement()
IL_003e:  ldc.i4.0
IL_003f:  ldelema    ['mscorlib']System.Int32
IL_0044:  box        ['mscorlib']System.Int32
IL_0049:  call  void ['mscorlib']System.Console::WriteLine
(class System.String, class System.Object)
IL_004e:  ret
} // end of method. 'CompoundAssignment1App::Main'
```

Looking at the boldface lines in this MSIL, you can see that the Compound-Assignment1App.GetArrayElements method is actually being called twice! In a best-case scenario, this is inefficient. In the worst case, it could be disastrous, depending on what else the method does.

Now take a look at the following code, and note the change of the assignment to the compound assignment operator syntax: -

```using System;
class CompoundAssignment2App
{
protected int[] elements;
public int[] GetArrayElement()
{
return elements;
}
CompoundAssignment2App()
{
elements = new int[1];
elements[0] = 42;
}
public static void Main()
{
CompoundAssignment2App app = new CompoundAssignment2App();
Console.WriteLine("{0}", app.GetArrayElement()[0]);
app.GetArrayElement()[0] += 5;
Console.WriteLine("{0}", app.GetArrayElement()[0]);
}
}
```

The use of the compound assignment operator results in the following much more efficient MSIL code:-

```// More efficient technique of x op= y.
.method public hidebysig static void Main() il managed
{
.entrypoint
// Code size       76 (0x4c)
.maxstack  4
.locals (class CompoundAssignment1App V_0, int32[] V_1)
IL_0000:  newobj     instance void CompoundAssignment1App::.ctor()
IL_0005:  stloc.0
IL_0006:  ldstr      "{0}"
IL_000b:  ldloc.0
IL_000c:  call instance int32[] CompoundAssignment1App::GetArrayElement()
IL_0011:  ldc.i4.0
IL_0012:  ldelema    ['mscorlib']System.Int32
IL_0017:  box        ['mscorlib']System.Int32
IL_001c:  call   void ['mscorlib']System.Console::WriteLine
(class System.String, class System.Object)
IL_0021:  ldloc.0
IL_0022:  call  instance int32[] CompoundAssignment1App::GetArrayElement()
IL_0027:  dup
IL_0028:  stloc.1
IL_0029:  ldc.i4.0
IL_002a:  ldloc.1
IL_002b:  ldc.i4.0
IL_002c:  ldelem.i4
IL_002d:  ldc.i4.5
IL_002e:  add
IL_002f:  stelem.i4
IL_0030:  ldstr      "{0}"
IL_0035:  ldloc.0
IL_0036:  call    instance int32[] CompoundAssignment1App::GetArrayElement()
IL_003b:  ldc.i4.0
IL_003c:  ldelema    ['mscorlib']System.Int32
IL_0041:  box        ['mscorlib']System.Int32
IL_0046:  call  void ['mscorlib']System.Console::WriteLine
(class System.String, class System.Object)
IL_004b:  ret
} // end of method 'CompoundAssignment1App::Main'
```

We can see that the MSIL dup opcode is being used. The dup opcode duplicates the top element on the stack, thereby making a copy of the value retrieved from the call to the CompoundAssignment1App.GetArrayElements method.

The point of this exercise has been to illustrate that although conceptually x += y is equivalent to x = x + y, subtle differences can be found in the generated MSIL. These differences mean you need to think carefully about which syntax to use in each circumstance. A basic rule of thumb, and my recommendation, is to use compound assignment operators whenever and wherever possible.

Advertisement:
Advertisement: