3

The following code works.

byte b = 1;

But I noticed the following code doesn't work

byte b = BooleanProperty ? 2 : 3; // error

The compiler says

Cannot convert source type 'int' to target type 'byte'

I understand that int type cannot be converted to byte type implicitly. But why the former code works, and the latter doesn't?

Soner Gönül
  • 97,193
  • 102
  • 206
  • 364
Shigure
  • 373
  • 4
  • 13
  • 1
    I am unable to find the correct C# specification references for this but when the value used in a variable declaration/initialization is constant, the compiler checks and coerces the value to the correct type, assuming it is possible (if not it is a compile-time error). Since the second example is not a compile-time constant, a normal expression is used instead in which numbers are types as `int` values by default. So basically, that it works in some cases is because the compiler is able to unequivocally deduce that the number is going to be both constant and in range. – Lasse V. Karlsen Jul 04 '15 at 21:10
  • 1
    @LasseV.Karlsen, I believe you are referring to section 6.1.8 of C# spec – Rahul Jul 04 '15 at 21:14
  • I'm not entirely sure because variable declarations that contain initialization values are somewhat exempt the normal rules of type coercion or conversion if everything is constant. I believe the main thing that makes one thing "work" and another thing "not work" is whether the expression is 100% constant or not (meaning, can the compiler deduce and evalute the constant value of the expression or not). – Lasse V. Karlsen Jul 04 '15 at 21:15
  • This is an area where the C# compiler isn't very helpful. If you give it a literal number, and the number is within the scope of the type, then it'll accept it as a valid assignment. In all other situations, the compiler casts those numbers to int32 and then refuses to implicitly cast them to the correct type, even if the number is in scope. It's a nuisance, but it'll likely never be fixed. – David Arno Jul 04 '15 at 21:18
  • @DavidArno I'm pretty sure that this is specified in the specification somewhere, but (seemingly) like you I'm unable to come up with a definite reference in the specification to the part that says this is so. – Lasse V. Karlsen Jul 04 '15 at 21:20
  • 1
    @LasseV.Karlsen, I think PetSerAl has it and I was slightly out in what I said, as expressed in *6.1.9 Implicit constant expression conversions*. All whole numbers without a suffix are treated as `Int32`, but the compiler will implicity cast back to the correct type if, and only if, its a literal or constant. – David Arno Jul 04 '15 at 21:26
  • 6.1.9 is indeed the right reference here, teaches me to scroll down when reading referenced material. – Lasse V. Karlsen Jul 04 '15 at 21:33
  • I voted to reopen because this question is not a duplicate of [this SO thread](http://stackoverflow.com/questions/1890390/how-to-use-cs-ternary-operator-with-two-byte-values). OP is not asking about the compiler error. He's asking why `byte b = 1;` compiles ok without explicit casting. – sstan Jul 04 '15 at 21:47

2 Answers2

6

There's an implicit conversion from int constants (not just literals, but any compile-time constant expression of type int) to byte, so long as the value is in range. This is from section 6.1.9 of the C# 5 specification:

An implicit constant expression conversion permits the following conversions:

  • A constant-expression (§7.19) of type int can be converted to type sbyte, byte, short, ushort, uint, or ulong, provided the value of the constant-expression is within the range of the destination type.

However, there's no implicit conversion from a "general" expression of type int to byte - and that's what you've got in your second case. It's a bit like this:

int tmp = BooleanProperty ? 2 : 3;
byte b = tmp; // Not allowed

Note that the use of the conditional expression doesn't play any part in inferring its type - and as both the second and third operands are of type int, the overall expression is of type int as well.

So if you understand why the snippet above where I've separated the code into two statements doesn't compile, that explains why your single-line version with the conditional doesn't either.

There are two ways of fixing it:

  • Change the second and third operands to expressions of type byte so that the conditional expression has an overall type of byte:

      byte b = BooleanProperty ? (byte) 2 : (byte) 3;
    
  • Cast the result of the conditional expression:

      byte b = (byte) (BooleanProperty ? 2 : 3);
    
Community
  • 1
  • 1
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
2

When it comes to literal integers in C#.

If the literal has no suffix, it has the first of these types in which its value can be represented: int, uint, long, ulong.

Compiler is smart enough to deduce that in case of byte b = 1; the literal fits the byte type. But it's not smart enough to figure it out in case of the conditional (ternary) operator ?:.

Community
  • 1
  • 1
Dzienny
  • 3,295
  • 1
  • 20
  • 30
  • Not disputing what you say (agree with it), but I find it really odd that a compiler smart enough to correctly infer types for some really quite complex expressions involving generics or lambda parameters cannot correctly infer the type for a ternary expression. Guess the compiler writers just never thought it important. – David Arno Jul 04 '15 at 21:30
  • 1
    @DavidArno [Minus 100 Points](/http://blogs.msdn.com/b/ericgu/archive/2004/01/12/57985.aspx) – Lasse V. Karlsen Jul 04 '15 at 21:32
  • @LasseV.Karlsen, many thanks for the link. – David Arno Jul 04 '15 at 21:46
  • @DavidArno That's the most likely case that the cost of implementing the feature exceeded the expected benefits. – Dzienny Jul 04 '15 at 21:53
  • Ugh, link not working so reposting: [Minus 100 Points](http://blogs.msdn.com/b/ericgu/archive/2004/01/12/57985.aspx). Not sure what I did wrong but sorry about that. – Lasse V. Karlsen Jul 04 '15 at 22:00