views:

71

answers:

2

Why is

byte someVar;
someVar -= 3; 

valid but

byte someVar;
someVar = someVar - 3;

isnt?

+6  A: 

Surprisingly, when you perform operations on bytes the computations will be done using int values, with the bytes implicitly cast to (int) first. This is true for shorts as well, and similarly floats are up-converted to double when doing floating-point arithmetic.

The second snippet is equivalent to:

byte someVar;
someVar = (int) someVar - 3;

Because of this you must cast the result back to (byte) to get the compiler to accept the assignment.

someVar = (byte) (someVar - 3);
John Kugelman
Can someone verify if the type casting happens at the IL level in the someVar-=3; example? Does it generate the same IL code?
Dested
I just checked with Reflector. The `-=` operator generates a `conv.u1` at the IL level, which is why the first snippet is working
Thomas Levesque
+4  A: 

Here's a copy of a table in the CLI specification (Ecma 335) that specifies which operands are valid on the binary numeric operators of the type A op B, where A and B are the operands and "op" is the operator, like Opcodes.Sub that you are using in your snippet:

alt text

Some annotations are required with this:

  • "native int" is IntPtr in a C# program
  • F represents a floating point type, double or float in C#
  • & represents a pointer value, the boxes are shaded because they are unsafe operations
  • O represents an object reference
  • x is an operation that's not permitted.

Note the row and column for F, both operands must be floating point, you cannot directly add, say, an int to a double. The C# compiler deals with that limitation by automatically converting the int operand to double so that the operator is valid.

Relevant to your question: also note that the byte, sbyte, char, short and ushort types are not present. Same approach, the compiler converts the operands to the smallest type that can represent the value so that the operator can be used. Which will be int32. According to the table, the result of the operation will be int32.

Now here's the rub: the result is int32 but assigning that back to a byte value requires a narrowing conversion. From 32 bits to 8 bits. That's trouble because it loses significant bits. The C# compiler requires you to make that explicit. You essentially acknowledge that you know what you're doing and that you are aware of the potentially surprising result. Like this one:

        byte v = 255;
        v = (byte)(v + 1);

The -= operator is a problem because there is no effective way to apply that required cast. It isn't expressible in the language syntax. Using (byte)3 doesn't make sense, the literal gets converted to int32 anyway to make the operator work.

They punted the problem, the compiler automatically emits the cast without your help.

Hans Passant