为什么将短空值转换为int null值以便与null进行比较?

时间:2012-02-09 19:24:19

标签: c# compiler-construction nullable

当我比较可空的短值时,编译器首先将它们转换为整数以与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){}进行简单的检查。至少这是我在发现这种转换后在代码中所做的事情。

为什么所有这些额外的代码都添加了这么简单的测试?

2 个答案:

答案 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,这也是他们所做的。在您提供的已编译代码中,数字促销只是先于可空行为。

至于编译器总是试图优化代码,专家可以再次澄清,但事实并非如此。它可以进行优化,而其他可能会延迟抖动。根据是否是调试与发布版本,无论是否附加调试器,都可以对编译器或抖动进行优化,也可以不进行优化。毫无疑问,他们可以做出优化,他们只是选择不这样做,因为成本与收益并没有发挥作用。