I converted a switch statement into an expression and expected value to have the same type. However the expression returns decimal type instead of int.
using System;
public class Program
{
public enum SomeType
{
INT32,
DECIMAL
}
public static void Main()
{
_ = Enum.TryParse("INT32", out SomeType paramType);
object? value = null;
//
value = paramType switch
{
SomeType.DECIMAL => (decimal)2.0,
SomeType.INT32 => (int)1,
_ => throw new ArgumentOutOfRangeException($"Not expected type.")
};
Console.WriteLine(value);
Console.WriteLine(value.GetType().ToString());
value = null;
switch(paramType)
{
case SomeType.DECIMAL:
value = (decimal)2.0;
break;
case SomeType.INT32:
value = (int)1;
break;
default:
throw new ArgumentOutOfRangeException($"Not expected type.");
}
Console.WriteLine(value);
Console.WriteLine(value.GetType().ToString());
}
}
Results:
1
System.Decimal
1
System.Int32
Why is that? You can try it here: https://dotnetfiddle.net/
CodePudding user response:
The switch expression tries to find the "best" result type, in the same way that an implicitly-typed array does.
Given types int
and decimal
, the best result type is decimal
, because while there's an implicit conversion from int
to decimal
, there's only an explicit conversion from decimal
to int
(as it loses information).
The switch expression will implicit convert the result of the selected "branch" to that type, regardless of type of the variable that it's being assigned to.
In other words, your code is equivalent to:
object? value = null;
// The compiler selects tmp here based as the best result type
// between int and decimal
decimal tmp = paramType switch { ... };
// Now the assignment performs any conversions required to assign
// to the variable
value = tmp;
If you wanted to make the result type of the switch expression object
, you need to make sure that one of the branches returns object
. For example:
value = paramType switch
{
SomeType.DECIMAL => (object) 2.0m,
SomeType.INT32 => 1,
_ => throw new ArgumentOutOfRangeException($"Not expected type.")
};
(The use of 2.0m is just to demonstrate a better way of coming up with a decimal
than casting a double
value...)
Now the result type is object
, and in your sample code you end up with a boxed int
instead of the int
value being converted to decimal
before boxing.