Question

This question already has an answer here:

I have this code.

byte dup = 0;
Encoding.ASCII.GetString(new byte[] { (0x80 | dup) });

When I try to compile I get:

Cannot implicitly convert type 'int' to 'byte'. An explicit conversion exists (are you missing a cast?)

Why does this happen? Shouldn't | two bytes give a byte? Both of the following work, assuring that each item is a byte.

Encoding.ASCII.GetString(new byte[] { (dup) });
Encoding.ASCII.GetString(new byte[] { (0x80) });
Was it helpful?

Solution

It's that way by design in C#, and, in fact, dates back all the way to C/C++ - the latter also promotes operands to int, you just usually don't notice because int -> char conversion there is implicit, while it's not in C#. This doesn't just apply to | either, but to all arithmetic and bitwise operands - e.g. adding two bytes will give you an int as well. I'll quote the relevant part of the spec here:

Binary numeric promotion occurs for the operands of the predefined +, –, *, /, %, &, |, ^, ==, !=, >, <, >=, and <= binary operators. Binary numeric promotion implicitly converts both operands to a common type which, in case of the non-relational operators, also becomes the result type of the operation. Binary numeric promotion consists of applying the following rules, in the order they appear here:

  • If either operand is of type decimal, the other operand is converted to type decimal, or a compile-time error occurs if the other operand is of type float or double.

  • Otherwise, if either operand is of type double, the other operand is converted to type double.

  • Otherwise, if either operand is of type float, the other operand is converted to type float.

  • Otherwise, if either operand is of type ulong, the other operand is converted to type ulong, or a compile-time error occurs if the other operand is of type sbyte, short, int, or long.

  • Otherwise, if either operand is of type long, the other operand is converted to type long.

  • Otherwise, if either operand is of type uint and the other operand is of type sbyte, short, or int, both operands are converted to type long.

  • Otherwise, if either operand is of type uint, the other operand is converted to type uint.

  • Otherwise, both operands are converted to type int.

I don't know the exact rationale for this, but I can think about one. For arithmetic operators especially, it might be a bit surprising for people to get (byte)200 + (byte)100 suddenly equal to 44, even if it makes some sense when one carefully considers the types involved. On the other hand, int is generally considered a type that's "good enough" for arithmetic on most typical numbers, so by promoting both arguments to int, you get a kind of "just works" behavior for most common cases.

As to why this logic was also applied to bitwise operators - I imagine this is so mostly for consistency. It results in a single simple rule that is common for all non-boolean binary types.

But this is all mostly guessing. Eric Lippert would probably be the one to ask about the real motives behind this decision for C# at least (though it would be a bit boring if the answer is simply "it's how it's done in C/C++ and Java, and it's a good enough rule as it is, so we saw no reason to change it").

OTHER TIPS

The literal 0x80 has the type "int", so you are not oring bytes.

That you can pass it to the byte[] only works because 0x80 (as a literal) it is within the range of byte.

Edit: Even if 0x80 is cast to a byte, the code would still not compile, since oring bytes will still give int. To have it compile, the result of the or must be cast: (byte)(0x80|dup)

byte dup = 0;
Encoding.ASCII.GetString(new byte[] { (byte)(0x80 | dup) });

The result of a bitwise Or (|) on two bytes is always an int.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top