当我比较可空的短值时,编译器首先将它们转换为整数以与null进行比较。例如,请考虑以下简单代码:
short? cTestA;
if (cTestA == null) { ... }
编译器将其转换为:
short? CS$0$0001 = cTestA;
int? CS$0$0002 = CS$0$0001.HasValue ? new int?(CS$0$0001.GetValueOrDefault()) : null;
if (!CS$0$0002.HasValue){ ... }
包括.NET 4在内的所有.NET版本都会出现这种情况。
我在这里缺少什么?仅针对HasValue检查进行双重转换的原因是什么?
我希望编译器能够使用.HasValue if (cTestA.HasValue){}
进行简单的检查。至少这是我在发现这种转换后在代码中所做的事情。
为什么所有这些额外的代码都添加了这么简单的测试?
答案 0 :(得分:37)
Re:您的最新更新:
这是可以为空的算术优化器中的一个错误。
当您执行以下操作时,可以为空的优化程序将删除不必要的转换为int?
:
short? s = null;
int? x = s + 1;
未优化的codegen完全相同:
short? s = null;
int? x;
int? temp = s.HasValue ? new int?((int)s.Value) : new int?();
x = temp.HasValue ? new int?(x.Value + 1) : new int?();
优化的codegen相当于:
short? s = null;
int? x;
x = s.HasValue ? new int?((int)s.Value + 1) : new int?();
但是,优化器包含一个错误;我们不会为了平等而删除不必要的转换。
感谢您引起我的注意;我们会为Roslyn修复它。我实际上是要在接下来的几周内为Roslyn编写可空的优化器。
更新:我确实写了优化器,如果你对它的工作原理感兴趣,我写了一系列关于它的文章,从这里开始:
http://ericlippert.com/2012/12/20/nullable-micro-optimizations-part-one/
答案 1 :(得分:29)
见C# 4.0 language specification的第4.1.5节。特别感兴趣的是:
C#支持九种整数类型:sbyte,byte, short ,ushort,int,uint,long,ulong和char。 [省略文字]
积分型一元和二元运算符始终使用 带符号32位精度,无符号32位精度,带符号64位 精度或无符号64位精度:
[省略要点]
对于二进制+, - ,*,/,%,&,^,|, == ,!=,>,<,> =和&LT = 运算符,操作数转换为类型T,其中T是第一个 int,uint,long和ulong可以完全代表所有可能的 两个操作数的值。然后使用该操作执行操作 类型T的精度,结果的类型是T(或bool为 关系运算符)。一个操作数不允许 用二元运算符输入long,另一个是ulong类型。
使用short的操作被提升为int,并且这些操作被提升为可以为空的对应物。 (这导致7.3.6.2和7.3.7节)
好的,这是设计,但仍然不明白他们为什么这样做,他们已经优化了字符串添加太多,为什么单独留下数字并为这个简单的比较添加更多代码
这就是语言的设计方式,并考虑了现代建筑的优化。在这方面并非具体,但请考虑Eric Lippert所说的here
算术永远不会在C#中做空。算术可以用ints,uints,longs和ulongs来完成,但算术永远不会在短时间内完成。短程提升为int,算术以整数形式完成,因为正如我之前所说,绝大多数算术计算都适合于int。绝大多数都不适合做空。在针对整数优化的现代硬件上,短算术可能较慢,而短算术不会占用更少的空间;它将在芯片上以整数或多头完成。
您的最新动态:
我希望编译器做的是使用.HasValue进行简单检查if(cTestA.HasValue){}至少这是我在发现此转换后对代码执行的操作。所以这就是我真的不明白为什么不做那么简单的想法,而是添加所有这些额外的代码。编译器总是尝试优化代码 - 为什么在这里避免使用简单的.HasValue检查。我肯定在这里遗漏了一些东西......
我将不得不推迟编译专家说明为什么他们选择转换而不是立即进行HasValue检查,除非说可能只是一个操作顺序。语言规范说二进制运算符操作数被提升,这就是他们在提供的代码片段中所做的。语言规范接着后来说,x == null
的检查,其中x是可以为空的值类型,可以转换为!x.HasValue
,这也是他们所做的。在您提供的已编译代码中,数字促销只是先于可空行为。
至于编译器总是试图优化代码,专家可以再次澄清,但事实并非如此。它可以进行优化,而其他可能会延迟抖动。根据是否是调试与发布版本,无论是否附加调试器,都可以对编译器或抖动进行优化,也可以不进行优化。毫无疑问,他们可以做出优化,他们只是选择不这样做,因为成本与收益并没有发挥作用。