3

In my code i have the following code:

Order = config.DeploymentSteps.Select(x => x.Order).DefaultIfEmpty().Max() + 1;

This gives me the error Cannot implicitly convert type 'int' to 'short'. As a Reference Order and x.Order are both shorts, and Max() is correctly returning a short (I have verified this). So I get it, it thinks the 1 is an integer and erroring. So I changed it to:

Order = config.DeploymentSteps.Select(x => x.Order).DefaultIfEmpty().Max() + (short)1;

I'm now still getting the same compile. So maybe it's not casting it right, so I tried changing it to

Order = config.DeploymentSteps.Select(x => x.Order).DefaultIfEmpty().Max() + Convert.ToInt16(1);

Yet I still get the same error. Finally I got it to work by converting the whole expression:

Order = Convert.ToInt16(config.DeploymentSteps.Select(x => x.Order).DefaultIfEmpty().Max() + 1);

Why can't I cast the 1 to a short and add it to another short, without casting the whole thing?

KallDrexx
  • 27,229
  • 33
  • 143
  • 254

3 Answers3

5

It is because short + short = int.

Eric Lippert explains it here.

He says:

Why is short plus short result in int?

Well, suppose short plus short was short and see what happens:

short[] prices = { 10000, 15000, 11000 }; short average = (prices[0] + prices[1] + prices[2]) / 3; And the average is, of course, -9845 if this calculation is done in shorts. The sum is larger than the largest possible short, so it wraps around to negative, and then you divide the negative number.

In a world where integer arithmetic wraps around it is much more sensible to do all the calculations in int, a type which is likely to have enough range for typical calculations to not overflow.

Community
  • 1
  • 1
kevev22
  • 3,737
  • 21
  • 32
  • 2
    Meh, doesn't really explain why int + int = int and not long. The real reason is that the CLI only allows a limited number of types on the Opcodes.Add instruction. Int, long, float, double and IntPtr. And the operands must be promoted if necessary to make them match. The C# language designers *did* make the decision to not silently truncate the result back to a smaller type. Unlike the vb.net designers. – Hans Passant Sep 21 '11 at 19:05
  • If the accepted answer to this question is simply quoted from the answer to another question, this should definitely be closed as duplicate. :P – Dan J Sep 21 '11 at 19:33
  • 1
    @Hans: The range on ints is in the billions. If you are the sort of person who is doing arithemtic on ints that might overflow an int then *you should be using longs in the first place*. – Eric Lippert Sep 21 '11 at 21:07
  • 3
    @Eric: If you are the sort of person who is doing arithmetic on shorts that might overflow a short then you should be using ints in the first place. Equally true. This is not the compiler being a busy-body overriding the foolish programmer, the fact is that there is no way to add two shorts, you can't generate the code for it. It *can* make a choice about what to do with a result that doesn't fit. – Hans Passant Sep 21 '11 at 21:37
  • 6
    @Hans: I don't see what the available IL operations have to do with anything. There is no IL opcode for adding two decimals together, but we cheerfully allow that in C#. There's no IL opcode for nullable arithmetic, and we cheerfully allow that too. C# semantics were designed with the typical use case scenarios of C# users in mind, not with what the available opcodes were. If there were likely to be a lot of C# users who do arithmetic in shorts, we'd have made sure that there were built-in operators on shorts regardless of what IL we'd have to generate, as we did with decimal. – Eric Lippert Sep 21 '11 at 21:43
  • @Eric: these choices are dominated by perf. If that wouldn't have mattered then surely C# would generate overflow exceptions instead of silently generating an inaccurate result. – Hans Passant Sep 21 '11 at 22:54
  • @Hans: C# *already* allows you to do arithmetic on shorts regardless of the IL available: all of the `op=` operators transparently cast results back to short, for instance. So `x += y` works just fine (though it will truncate the results on overflow). To your comment about perf, note that you can always compile your entire assembly to use checked arithmetic if you like. Most people don't do this; not so much because of perf - but because overflows are relatively rare, and having to handle potential overflow exceptions everywhere (especially non-obvious cases) saps productivity and bloats code. – LBushkin Sep 22 '11 at 15:04
  • 2
    @Hans: Of course performance is a factor in the design. C# chose to have unchecked 32 bit arithmetic by default in large part because on most chips, *the jitted code* is fast, not because there happens to be an IL instruction for it. I'm just not following your argument that the C# semantics were driven by the available opcodes. And remember, we're all in the same division here; if the early C# designers felt that we'd needed an IL opcode to add two shorts then the CLR would have such an opcode! – Eric Lippert Sep 22 '11 at 15:07
  • @Eric: That's my exact argument. It chose unchecked 32-bit arith because 32-bit processors are particularly fast at it. Nothing to do with short and byte range. – Hans Passant Sep 22 '11 at 16:34
1

As much as it sounds redundant, would you mind declaring a short variable (or perhaps a const) and initilize it to 1 and use that while assigning your Order variable. Unlike an int or long, there is no literal way of specifying a short.

Arun
  • 2,493
  • 15
  • 12
  • The problem is that these are shorts because my database column for this field is a `smallInt` – KallDrexx Sep 21 '11 at 18:53
  • Understood. But if you can't change that you still do not have a solution correct? That is why I was suggesting you to use a short while you do a Select, as in: Order = config.DeploymentSteps.Select(x => x.Order).DefaultIfEmpty().Max() + yourShortVariableOrConst; // which is initialzed to a 1 – Arun Sep 21 '11 at 19:10
  • Why doesn't my final bit of code work for my needs, where the whole operation is inside a `Convert.ToInt16` ? – KallDrexx Sep 21 '11 at 19:18
  • In your final bit of code, you are attempting to convert the Result of the output from the Select method. However, the issue is NOT there. The issue is at the + operand when it tries to add a short to an int (literal 1 is implicitly assumed to be an int). – Arun Sep 21 '11 at 19:23
1

The C# language specification (download link) lists the predefined addition operators. For integral types, these are:

int operator +(int x, int y);
uint operator +(uint x, uint y);
long operator +(long x, long y);
ulong operator +(ulong x, ulong y);

The reason you have to cast is because there's no specific operator to add shorts. I know that "because C# is made to work like this" isn't a particularly useful answer but there you go. I'd just go with:

Order = (short) config.DeploymentSteps.Select(x => x.Order).DefaultIfEmpty().Max() + 1;

or:

Order = config.DeploymentSteps.Select(x => x.Order).DefaultIfEmpty().Max();
Order++;
millimoose
  • 39,073
  • 9
  • 82
  • 134