Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is x = x + 100 treated differently than x += 100 that compiles to the same IL?

Tags:

c#

casting

We know that these two addition statements are equivalent and compile to the same IL code:

int x = 100;

x += 100;
x = x + 100;

However, when there is an explicit cast required I have noticed something strange:

byte b = 100;

b += 200; // Compiles (1)
b = b + 200; // Cannot implicitly convert int to byte (2)
b = (byte) (b + 200); // Compiles (3)

It is obvious why the second statement requires an explicit cast because the result of the addition is an integer. But the weird thing to me is the first statement. It compiles to the exact same IL as the third statement, so it looks like compiler adds a cast that is supposed to be explicit, for us. But it can't do it in the second statement.

It seems contradictory to me because I would expect the first statement to be equivalent to the second and never compile, so why does it compile?

Note: This doesn't compile when an explicit cast is required from long to int:

int x = 100;
long y = 200;

x += y;
like image 240
Selman Genç Avatar asked Sep 09 '18 01:09

Selman Genç


1 Answers

You really need to go to the specs for this sort of information (and it can be really hard to get your head around the wording). However, straight from the horses mouth

12.18.3 Compound assignment

An operation of the form x op= y is processed by applying binary operator overload resolution (§12.4.5) as if the operation was written x op y. Then,

  • If the return type of the selected operator is implicitly convertible to the type of x, the operation is evaluated as x = x op y, except that x is evaluated only once.

  • Otherwise, if the selected operator is a predefined operator, if the return type of the selected operator is explicitly convertible to the type of x , and if y is implicitly convertible to the type of x or the operator is a shift operator, then the operation is evaluated as x = (T)(x op y), where T is the type of x, except that x is evaluated only once.

  • Otherwise, the compound assignment is invalid, and a binding-time error occurs.

...

blah blah blah

...

The second rule above permits x op= y to be evaluated as x = (T)(x op y) in certain contexts. The rule exists such that the predefined operators can be used as compound operators when the left operand is of type sbyte, byte, short, ushort, or char. Even when both arguments are of one of those types, the predefined operators produce a result of type int, as described in §12.4.7.3. Thus, without a cast it would not be possible to assign the result to the left operand.

The intuitive effect of the rule for predefined operators is simply that x op= y is permitted if both of x op y and x = y are permitted.

byte b = 0;
char ch = '\0';
int i = 0;
b += 1; // Ok
b += 1000; // Error, b = 1000 not permitted
b += i; // Error, b = i not permitted
b += (byte)i; // Ok
ch += 1; // Error, ch = 1 not permitted
ch += (char)1; // Ok

the intuitive reason for each error is that a corresponding simple assignment would also have been an error.

In short, computer says no.

like image 76
TheGeneral Avatar answered Nov 08 '22 04:11

TheGeneral