.Nat

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

环境是.net Framework 3.5
考虑到这样一组代码:
Type a = new Type();
Type b = new Type();
a = a ?? b;

Type a = new Type();
Type b = new Type();
a = (null == a ? b : a);
在正常情况下, 二者的结果是一样的, 可是谁的效率更高呢?

设计如下的代码实验:
code snippet A:
{
int? a;
int? b = 3;
a = b;
a = a ?? b;
}
其il代码如下
{
  .entrypoint
  // Code size       40 (0x28)
  .maxstack  3
  .locals init ([0] valuetype [mscorlib]System.Nullable`1<int32> a,
           [1] valuetype [mscorlib]System.Nullable`1<int32> b,
           [2] valuetype [mscorlib]System.Nullable`1<int32> CS$0$0000)
  IL_0000:  nop
  IL_0001:  ldloca.s   b
  IL_0003:  ldc.i4.3
  IL_0004:  call       instance void valuetype [mscorlib]System.Nullable`1<int32>::.ctor(!0)
  IL_0009:  nop
  IL_000a:  ldloc.1
  IL_000b:  stloc.0
  IL_000c:  ldloc.0
  IL_000d:  stloc.2
  IL_000e:  ldloca.s   CS$0$0000
  IL_0010:  call       instance bool valuetype [mscorlib]System.Nullable`1<int32>::get_HasValue()
  IL_0015:  brtrue.s   IL_001a
  IL_0017:  ldloc.1
  IL_0018:  br.s       IL_0026
  IL_001a:  ldloca.s   CS$0$0000
  IL_001c:  call       instance !0 valuetype [mscorlib]System.Nullable`1<int32>::GetValueOrDefault()
  IL_0021:  newobj     instance void valuetype [mscorlib]System.Nullable`1<int32>::.ctor(!0)
  IL_0026:  stloc.0
  IL_0027:  ret
}

而code snippet B:
{
int? a;
int? b = 3;
a = b;
a = (null == a ? b : a);
}
其il代码如下
{
  .entrypoint
  // Code size       27 (0x1b)
  .maxstack  2
  .locals init ([0] valuetype [mscorlib]System.Nullable`1<int32> a,
           [1] valuetype [mscorlib]System.Nullable`1<int32> b)
  IL_0000:  nop
  IL_0001:  ldloca.s   b
  IL_0003:  ldc.i4.3
  IL_0004:  call       instance void valuetype [mscorlib]System.Nullable`1<int32>::.ctor(!0)
  IL_0009:  nop
  IL_000a:  ldloc.1
  IL_000b:  stloc.0
  IL_000c:  ldloca.s   a
  IL_000e:  call       instance bool valuetype [mscorlib]System.Nullable`1<int32>::get_HasValue()
  IL_0013:  brfalse.s  IL_0018
  IL_0015:  ldloc.0
  IL_0016:  br.s       IL_0019
  IL_0018:  ldloc.1
  IL_0019:  stloc.0
  IL_001a:  ret
}
两者在000b也就是 a = b 之前都是相同的, 包括二者的判断代码也类似, 虽然一个走的是判断是否为真的路子而一个走的是是否为假, 这都正常. 不同点出现在判断之前和之后的赋值上. 其实很明显可以看出来snippet1的il里面都还存了一个loc.2, 索引为CS$0$0000. 但是a = a ?? b所作的是将a存在loc.2里, 由索引CS$0$0000进行是否为空的判断, 而CS$0$0000默认就是指向a的. 也就是snippet1不像是snippet2里那样做的, 直接load索引a对其进行是否为空的判断, 而是把它当成一个临时函数, 存入一个临时变量也就是CS$0$0000, null coalescing的开销要大于ternary conditional. 而且就算是DateTime?这样的复杂类型也是这样, 奇怪吧.
由于int? aka Nullable<int>是值类型, 我们再看一个引用类型的例子.

我们可以写一个空的class A, 考虑以下代码
考虑到这样一组代码:
A a = new A();
A b = new A();
a = a ?? b;
with il
{
.entrypoint
  // Code size       23 (0x17)
  .maxstack  2
  .locals init ([0] class ExperimentalOne.Program/A a,
           [1] class ExperimentalOne.Program/A b)
  IL_0000:  nop
  IL_0001:  newobj     instance void ExperimentalOne.Program/A::.ctor()
  IL_0006:  stloc.0
  IL_0007:  newobj     instance void ExperimentalOne.Program/A::.ctor()
  IL_000c:  stloc.1
  IL_000d:  ldloc.1
  IL_000e:  stloc.0
  IL_000f:  ldloc.0
  IL_0010:  dup
  IL_0011:  brtrue.s   IL_0015
  IL_0013:  pop
  IL_0014:  ldloc.1
  IL_0015:  stloc.0
  IL_0016:  ret
}

A a = new A();
A b = new A();
a = (null == a ? b : a);
with il
{
.entrypoint
  // Code size       24 (0x18)
  .maxstack  2
  .locals init ([0] class ExperimentalOne.Program/A a,
           [1] class ExperimentalOne.Program/A b)
  IL_0000:  nop
  IL_0001:  newobj     instance void ExperimentalOne.Program/A::.ctor()
  IL_0006:  stloc.0
  IL_0007:  newobj     instance void ExperimentalOne.Program/A::.ctor()
  IL_000c:  stloc.1
  IL_000d:  ldloc.1
  IL_000e:  stloc.0
  IL_000f:  ldloc.0
  IL_0010:  brfalse.s  IL_0015
  IL_0012:  ldloc.0
  IL_0013:  br.s       IL_0016
  IL_0015:  ldloc.1
  IL_0016:  stloc.0
  IL_0017:  ret
}

在这里, 代码却被做了优化, 由于少做了一次判断, ??怎么也比?:快, 如果用string或者复杂类型的class做对比也是这样.

我不知道编译器究竟是怎么想的才把机制设定成这样, 就算我加上ref关键字也同样表现. 我想以后我在写Nullable的值类型设计是否为空判断的时候会用?:, 而引用类型则是??了, 当然如果team doc里要求代码一致性不能这么写的话那就另当别论了-_-

posted on 2009-08-03 18:44  .Nat  阅读(168)  评论(0)    收藏  举报