146

Why does the first and second Write work but not the last? Is there a way I can allow all 3 of them and detect if it was 1, (int)1 or i passed in? And really why is one allowed but the last? The second being allowed but not the last really blows my mind.

Demo to show compile error

using System;
class Program
{
    public static void Write(short v) { }
    static void Main(string[] args)
    {
        Write(1);//ok
        Write((int)1);//ok
        int i=1;
        Write(i);//error!?
    }
}
CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
  • 2
    I too am stumped by this, i often have to cast ints to short in function calls although they should be castable... – Mathieu Dumoulin Jul 11 '12 at 12:19
  • 2
    @MathieuDumoulin they are castable, that's why you can cast them. But it's a lossfull conversion (there are many ints that don't fit in a short), so implicit cast is not possible, that's why you have to write `(short) i`. – Abel Jul 19 '12 at 08:25

8 Answers8

186

The first two are constant expressions, the last one isn't.

The C# specification allows an implicit conversion from int to short for constants, but not for other expressions. This is a reasonable rule, since for constants the compiler can ensure that the value fits into the target type, but it can't for normal expressions.

This rule is in line with the guideline that implicit conversions should be lossless.

6.1.8 Implicit constant expression conversions

An implicit constant expression conversion permits the following conversions:

  • A constant-expression (§7.18) 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.
  • A constant-expression of type long can be converted to type ulong, provided the value of the constant-expression is not negative.

(Quoted from C# Language Specification Version 3.0)

Community
  • 1
  • 1
CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
67

There is no implicit conversion from int to short because of the possibility of truncation. However, a constant expression can be treated as being of the target type by the compiler.

1? Not a problem: it’s clearly a valid short value. i? Not so much – it could be some value > short.MaxValue for instance, and the compiler cannot check that in the general case.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • So... it does not matter how explicit i am... >_<. Do you have any idea if i can detect if a litereal was passed or a int variable? –  Jul 11 '12 at 12:21
  • @acidzombie24 You cannot. But why would you want to do that? – Adam Houldsworth Jul 11 '12 at 12:21
  • @acidzombie24 I don’t think you can detect that. You *can* however use a template argument and then use reflection to get at its type. – Konrad Rudolph Jul 11 '12 at 12:22
  • 3
    @acidzombie24 There is no way a literal could to be passed in during runtime. So you can just use your eyes to check at compile time. – Justin Jul 11 '12 at 12:22
  • @AdamHouldsworth Sometimes I want functions to only take explicit int types (say i am writing to a file and used 1 instead of (byte)1). –  Jul 11 '12 at 12:24
  • @acidzombie24 Unfortunately the cast operators are going to stop you there. I could create a `public static implicit operator int(FooContainingNonInts f)` and you'd not be able to stop me! Mwuahhahhahhaa! But in all seriousness, what would you gain from being able to stop that? – Adam Houldsworth Jul 11 '12 at 12:24
  • FooContainingNonInts isn't a literal ;) which is what i want to check. In my case i wouldnt mind FooContainingNonInts bc its hardly an accident if the return type is int vs short –  Jul 11 '12 at 12:27
  • @KonradRudolph template argument. Hmm, i think you're saying i can check via runtime but not compile time :( –  Jul 11 '12 at 12:28
  • 1
    @acidzombie24 In that case, would it be an option to accept the argument as an `Expression>`? Then you could pass `() => 1` or `() => i` and inside the function you could inspect whether the passed entity contained a captured variable or a constant value. – Konrad Rudolph Jul 11 '12 at 12:28
  • [There you go.](http://ideone.com/QR50g) (Note, this is still at runtime, not compile time; also, note that expression arguments are treated still differently). Sorry it took so long – had to do it in MonoDevelop without IntelliSense. :-/ Forget the upvotes. – Konrad Rudolph Jul 11 '12 at 12:43
  • Your answer is slightly wrong. You shouldn't use *literal* but *constant-expression*. In particular the second example is not a *literal*. – CodesInChaos Jul 11 '12 at 13:16
8

an int literal can be implicitly converted to short. Whereas:

You cannot implicitly convert nonliteral numeric types of larger storage size to short

So, the first two work because the implicit conversion of literals is allowed.

Damien_The_Unbeliever
  • 234,701
  • 27
  • 340
  • 448
  • 3
    Your answer is slightly wrong. You shouldn't use *literal* but *constant-expression*. In particular the second example is not a *literal*. – CodesInChaos Jul 11 '12 at 13:17
6

I believe it is because you are passing in a literal/constant in the first two, but there is not automatic type conversion when passing in an integer in the third.

Edit: Someone beat me to it!

Justin
  • 6,373
  • 9
  • 46
  • 72
4

The compiler has told you why the code fails:

cannot convert `int' expression to type `short'

So here's the question you should be asking: why does this conversion fail? I googled "c# convert int short" and ended up on the MS C# page for the short keyword:

http://msdn.microsoft.com/en-us/library/ybs77ex4(v=vs.71).aspx

As this page says, implicit casts from a bigger data type to short are only allowed for literals. The compiler can tell when a literal is out of range, but not otherwise, so it needs reassurance that you've avoided an out-of-range error in your program logic. That reassurance is provided by a cast.

Write((short)i)
3

Because there will not be any implicit conversion between Nonliteral type to larger sized short.

Implicit conversion is only possible for constant-expression.

public static void Write(short v) { }

Where as you are passing integer value as an argument to short

int i=1;
Write(i);  //Which is Nonliteral here
Vishal Suthar
  • 17,013
  • 3
  • 59
  • 105
0

Converting from int -> short might result in data truncation. Thats why.

ak1238
  • 9
  • 2
0

Conversion from short --> int happens implicitly but int -> short will throw compile error bcoz it might result in data truncation.