在Visual Studio中使用C#8.0优化切换案例后出现意外结果
今天在编写代码时,VisualStudio通知我我的开关箱可以优化。但是,我使用的代码与VisualStudio从我的切换案例中生成的代码的对比结果并不相同 我使用的枚举:在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) {
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的了解还不够,不知道会是什么。