views:

323

answers:

1

Given the following routine:

private static object ParseNumber(string token, FieldDefinition def)
{
  if (def.Fraction > 0)
    return Double.Parse(token);
  else
    return Int64.Parse(token);
}

Resharper offers me the option to refactor it into a statement with the ternary operator:

private static object ParseNumber(string token, FieldDefinition def)
{
  return def.Fraction > 0 ? Double.Parse(token) : Int64.Parse(token);
}

Who can spot the trap?

+6  A: 

Okay, change to previous answer. Because there's an implicit conversion from Int64 to Double (but not vice versa), that will be the result type of the expression. So when you expect to get a boxed Int64, you actually get a boxed Double (but with a value which originally came from Int64.Parse).

Just in case that's not clear enough, let's change all the return statements such that they just return a variable. Here's the original code:

private static object ParseNumber(string token, FieldDefinition def)
{
  if (def.Fraction > 0)
    return Double.Parse(token);
  else
    return Int64.Parse(token);
}

Convert that appropriately:

private static object ParseNumber(string token, FieldDefinition def)
{
  if (def.Fraction > 0)
  {
    double d = Double.Parse(token);
    object boxed = d; // Result is a boxed Double
    return boxed;
  }
  else
  {
    long l = Int64.Parse(token);
    object boxed = l; // Result is a boxed Int64
    return boxed;
  }
}

And now let's do the same to the version with the conditional operator:

private static object ParseNumber(string token, FieldDefinition def)
{
  return def.Fraction > 0 ? Double.Parse(token) : Int64.Parse(token);
}

becomes

private static object ParseNumber(string token, FieldDefinition def)
{
  // The Int64.Parse branch will implicitly convert to Double
  double d = def.Fraction > 0 ? Double.Parse(token) : Int64.Parse(token);
  object boxed = d; // *Always* a Double
  return boxed;
}

EDIT: As requested, a bit more information. The type of a conditional expression of the form

X ? Y : Z

depends on the types of Y and Z, which I'll call TY and TZ. There are a few options:

  • TY and TZ are the same type: result is that type
  • There's an implicit conversion from TY to TZ but not from TZ to TY: the result is of type TZ and the conversion is used if the first branch is used.
  • There's an implicit conversion from TZ to TY but not from TY to TZ: the result is of type TY and the conversion is used if the second branch is used.
  • There's an implicit conversion in both directions: compile-time error
  • There are no conversions either way: compile-time error

Does that help?

Jon Skeet
This one got me before...
leppie
I posted it about 6 months back on CodeProject. In fact, it is scary that an integer is implicitly a double, even though it loose precision.
leppie
Could you maybe add a sentence or two about why the implicit conversion to double occurs? (presumably some property of the ternary/conditional operator)
Miles