rpeshkov
rpeshkov

Reputation: 5047

C# byte addition operation

Could someone point me, why here:

Byte b = 100; 
b = (Byte)(b+200);

I have to use explicit type conversion. But here

Byte b = 100;
b += 200;

I don't need to do this?

Does compiler generate different IL code for this two cases? And which case is better?

Upvotes: 7

Views: 5589

Answers (4)

Hans Passant
Hans Passant

Reputation: 941585

This is a FAQ in the C# tag, hard to find the duplicate. The need for the cast is relevant first. The underlying reason is that the CLI only specifies a limited number of valid types for the Opcodes.Add IL instruction. Only operands of type Int32, Int64, Single, Double and IntPtr are supported. IntPtr is special as well, the C# language forbids using that one.

So the C# compiler has to use an implicit conversion to uplift the byte to a type that the operator supports. It will pick Int32 as the closest compatible type. The result of the addition is Int32. Which does not fit back into a byte without truncating the result, throwing away the extra bits. An obvious example is 255 + 1, the result is 256 in Int32 but doesn't fit a Byte and yields 0 when stored.

That's a problem, the language designers didn't like that truncation to happen without you explicitly acknowledging that you are aware of the consequences. A cast is required to convince the compiler that you're aware. Bit of a cop-out of course, you tend to produce the cast mechanically without thinking much about the consequences. But that makes it your problem, not Microsoft's :)

The rub was the += operator, a very nice operator to write condense code. Resembles the brevity of the var keyword. Rock and a hard place however, where do you put the cast? It just doesn't work so they punted the problem and allowed truncation without a cast.

Notable is the way VB.NET works, it doesn't require a cast. But it gives a guarantee that C# doesn't provide by default, it will generate an OverflowException when the result doesn't fit. Pretty nice, but that check doesn't come for free.

Designing clean languages is a very hard problem. The C# team did an excellent job, warts not withstanding. Otherwise the kind of warts brought on by processor design. IL has these type restrictions because that's what real 32-bit processors have too, particularly the RISC designs that were popular in the 90s. Their internal registers can only handle 32-bit integers and IEEE-754 floating point. And only permit smaller types in loads and stores. The Intel x86 core is very popular and actually permits basic operations on smaller types. But that's mostly a historical accident due to Intel keeping the design compatible through the 8-bit 8080 and 16-bit 8086 generations. It doesn't come for free, 16-bit operations costs an extra cpu cycle. To be avoided.

Upvotes: 6

Mehrdad Afshari
Mehrdad Afshari

Reputation: 422006

Because the standard permits it (see the second case below):

14.14.2 Compound assignment

An operation of the form x op= y is processed by applying binary operator overload resolution (§14.2.4) 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 compile-time error occurs.

The IL code should be essentially identical in this case. Of course, if evaluating b has side effects, it will be evaluated twice in the b = (byte)b + 200 case and only once when using compound assignment.

Upvotes: 15

Petar Ivanov
Petar Ivanov

Reputation: 93030

This because of the implicit conversion rules.

When you have a binary + operator the result is converted to the larger of the two types. The literal 200 is of type int and therefore the type of the expression b+200 is int. The assignment operator = doesn't do implicit conversion, but rather throws error. As in

        int x = 10;
        Byte b = x; //Error

In the second case the += operator expects byte, so 200 (which is of type int, but fits into byte) is implicitly converted to byte, because the compiler knows it can. The following won't compile, because the compile doesn't know if x will fit in a byte or not.

        Byte b = 100;
        int x = 200;
        b += x; //Error

If you make x a const then it compiles:

        Byte b = 100;
        const int x = 200;
        b += x;  //OK

Upvotes: 0

Joe
Joe

Reputation: 42627

The spec calls this out as a specific case of the compound assignment operator:

http://msdn.microsoft.com/en-us/library/aa691316%28v=vs.71%29.aspx

Specifically, bullet 2:

  • 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, 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.

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 Section 7.2.6.2. Thus, without a cast it would not be possible to assign the result to the left operand.

Upvotes: 0

Related Questions