使用 C#8.0 在 Visual Studio 中优化 switch case 后出现意外结果

Juc*_*een 18 c# c#-8.0

今天在编码时,visual studio 通知我我的 switch case 可以优化。但是我拥有的代码与 Visual Studio 从我的 switch 案例生成的代码不会产生相同的结果。

我使用的枚举:

public enum State
{
    ExampleA,
    ExampleB,
    ExampleC
};
Run Code Online (Sandbox Code Playgroud)

以下代码运行后,该值等于 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;
}
Run Code Online (Sandbox Code Playgroud)

但是当visual studio优化switch case的时候,这个值变成了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()
};
Run Code Online (Sandbox Code Playgroud)

这只是带有重现错误输出信息的代码,而不是生产中运行的实际代码。我发现奇怪的是,如果我注释掉State.ExampleA最后一个代码块中的行,就会写入正确的值。

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

Swe*_*per 27

这突出了语句表达式之间的区别。你之前的 switch 是一个 switch语句,这是运行的分配。

value = BitConverter.ToUInt32(BitConverter.GetBytes((long)2147483647), 0);
Run Code Online (Sandbox Code Playgroud)

在这里,您将uint(右侧)转换为double(左侧)。您实际上在 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()
}
Run Code Online (Sandbox Code Playgroud)

开关的每一个分支返回不同的类型- floatuintshort分别。所以 C# 需要找到一种类型,所有这三个都可以隐式转换为。而float被发现。C# 不能只是“弄清楚”开关在运行时返回什么,并计算出“动态”执行的转换。

每个分支中返回的东西必须首先转换为float. 因此,整个表达式的类型是float。最后,您将 分配floatvalue,这是一个double

所以整体转换是uint-> float-> double,导致精度损失。


Gur*_*ron 7

这将起作用:

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()
    };
Run Code Online (Sandbox Code Playgroud)

BitConverter.ToSingle 返回float所以编译器推断 float(之间floatuintshort作为输出型的开关式(铸造)uintshort到它),然后投射它的结果double,其结果在精度损失ExampleB的情况下。