在Visual Studio中使用C#8.0优化切换案例后出现意外结果

在Visual Studio中使用C#8.0优化切换案例后出现意外结果,c#,c#-8.0,C#,C# 8.0,今天在编写代码时,VisualStudio通知我我的开关箱可以优化。但是,我使用的代码与VisualStudio从我的切换案例中生成的代码的对比结果并不相同 我使用的枚举: public enum State { ExampleA, ExampleB, ExampleC }; 运行以下代码后,该值等于2147483647 State stateExample = State.ExampleB; double value; switch (stateExample) {

今天在编写代码时,VisualStudio通知我我的开关箱可以优化。但是,我使用的代码与VisualStudio从我的切换案例中生成的代码的对比结果并不相同

我使用的枚举:

public enum State
{
    ExampleA,
    ExampleB,
    ExampleC
};
运行以下代码后,该值等于2147483647

State stateExample = State.ExampleB;
double value;

switch (stateExample)
{
    case State.ExampleA:
        value = BitConverter.ToSingle(BitConverter.GetBytes((long)2147483646), 0);
        break;
    case State.ExampleB:
        value = BitConverter.ToUInt32(BitConverter.GetBytes((long)2147483647), 0);
        break;
    case State.ExampleC:
        value = BitConverter.ToInt16(BitConverter.GetBytes((long)2147483648), 0);
        break;
    default:
        value = 0;
        break;
}
但是,当VisualStudio优化开关案例时,该值变为2147483648

State stateExample = State.ExampleB;
double value = stateExample switch
{
    State.ExampleA => BitConverter.ToSingle(BitConverter.GetBytes((long)2147483646), 0), //Commenting this line results in correct value
    State.ExampleB => BitConverter.ToUInt32(BitConverter.GetBytes((long)2147483647), 0),
    State.ExampleC => BitConverter.ToInt16(BitConverter.GetBytes((long)2147483648), 0),
    _ => throw new InvalidOperationException()
};
这只是包含再现错误输出信息的代码,而不是在生产中运行的实际代码。我发现奇怪的是,如果我注释掉最后一个代码块中的行
State.ExampleA
,就会写出正确的值

我的问题是:这是一个bug吗?还是我在这里遗漏了什么?

这样可以:

double value1 = stateExample switch
    {
        State.ExampleA => (double)BitConverter.ToSingle(BitConverter.GetBytes((long)2147483646), 0), //Commenting this line results in correct value
        State.ExampleB => BitConverter.ToUInt32(BitConverter.GetBytes((long)2147483647), 0),
        State.ExampleC => BitConverter.ToInt16(BitConverter.GetBytes((long)2147483648), 0),
        _ => throw new InvalidOperationException()
    };

BitConverter.ToSingle
返回
float
so编译器
float
(介于
float
uint
short
之间)作为开关表达式的输出类型(并将
uint
short
转换为它)然后将其结果强制转换为
double
,这将导致
ExampleB
案例的精度损失。

这突出了语句和表达式之间的差异。您之前使用的开关是一个开关语句,这是运行的赋值

value = BitConverter.ToUInt32(BitConverter.GetBytes((long)2147483647), 0);
这里您正在将
uint
(右侧)转换为
双精度
(左侧)。实际上,在switch语句的每个分支中都进行了不同的转换,这很好,因为它们是单独的赋值语句

将其与优化后的操作进行比较:switch语句变成了switch表达式。表达式只有一种类型。这个表达式的类型是什么

stateExample switch
{
    State.ExampleA => BitConverter.ToSingle(BitConverter.GetBytes((long)2147483646), 0), //Commenting this line results in correct value
    State.ExampleB => BitConverter.ToUInt32(BitConverter.GetBytes((long)2147483647), 0),
    State.ExampleC => BitConverter.ToInt16(BitConverter.GetBytes((long)2147483648), 0),
    _ => throw new InvalidOperationException()
}
开关的每个分支分别返回不同的类型-
float
uint
short
因此C#需要找到一种类型,这三种类型都可以隐式转换为。并找到
float
。C#不能只是“弄清楚”开关在运行时返回什么,并计算出转换以“动态”执行

每个分支中返回的内容必须首先转换为
浮点值
。因此,整个表达式的类型是
float
。最后,将
浮点
分配给
,这是一个
双精度


因此,整体转换为
uint
->
float
->
double
,导致精度损失。

将提供正确的结果。但是,我不知道为什么我会认为重构是无效的,因为它引入了一个中间转换,将
Single
转换为整个表达式的类型,并且失去了一些精度。它必须从
single
UInt32
Int16
中选择一种最好的类型。我希望visual studio中有某种信息能够说明此更改,或者它能够更好地检测到此更改。在这件事之后,我宁愿在盲目使用visual studio的优化功能之前三思而后行…@JuckoThirteen事实上,那些优化建议只是那些建议。在许多情况下,不应遵循这些原则。例如,这个特定的实例并不适用于所有的C#版本。我经常使用Unity引擎,它根本不支持模式匹配,但VS仍然很高兴地建议进行此更改,它甚至不会在目标平台上编译。@Darrel您确定设置的目标语言版本不正确吗?Roslyn和R#通常都擅长只提供语言版本适用的重构。除此之外,从int到float的整个隐式转换对我来说总是一个奇怪的选择,正是因为这些原因-lossful隐式转换是愚蠢的。@Voo Unity专门自动设置项目文件,所以如果设置为错误的版本,那就很奇怪了。不可否认,这与其说是VS问题,不如说是团结问题。@Darrel当时可能确实是团结问题。VS的确正确地考虑了,并且没有提供.NET 4.5.2-我刚才尝试过的重构提示(NETCOREAPP3.1,我确实得到了提示,NET452我没有)。作为一种解决方法,您可以显式地设置语言版本,但您对unity的了解还不够,不知道会是什么。