【发布时间】:2010-02-12 11:07:43
【问题描述】:
鉴于这是一个非常自然的用例(如果您不知道 as 实际做了什么),
if (x is Bar) {
Bar y = x as Bar;
something();
}
实际上等价于(也就是说,编译器从上述代码生成的CIL 将等价于):
Bar y = x as Bar;
if (y != null) {
y = x as Bar; //The conversion is done twice!
something();
}
编辑:
我想我的问题没有说清楚。我永远不会写第二个 sn-p 因为它当然是多余的。我声称编译器在编译第一个 sn-p 时生成的 CIL 等效于第二个 sn-p,这是多余的。问题: a) 这是正确的吗? b) 如果是这样,为什么is 是这样实现的?
这是因为我发现第一个 sn-p 比实际写得好得多清晰和漂亮
Bar y = x as Bar;
if (y != null) {
something();
}
结论:
优化 is/as 的情况不是编译器的责任,而是 JIT 的责任。
此外,与空值检查相比,它比两种替代方法(is 和 as 和 is 和 cast)具有更少(且成本更低)的指令。
附录:
CIL for as with nullcheck (.NET 3.5):
L_0001: ldarg.1
L_0002: isinst string
L_0007: stloc.0
L_0008: ldloc.0
L_0009: ldnull
L_000a: ceq
L_000c: stloc.1
L_000d: ldloc.1
L_000e: brtrue.s L_0019
L_0011: ldarg.0
L_0019: ret
适用于 is 和 cast (.NET 3.5) 的 CIL:
L_0001: ldarg.1
L_0002: isinst string
L_0007: ldnull
L_0008: cgt.un
L_000a: ldc.i4.0
L_000b: ceq
L_000d: stloc.1
L_000e: ldloc.1
L_000f: brtrue.s L_0021
L_0012: ldarg.1
L_0013: castclass string
L_0018: stloc.0
L_0019: ldarg.0
L_0021: ret
is 和 as (.NET 3.5) 的 CIL:
L_0001: ldarg.1
L_0002: isinst string
L_0007: ldnull
L_0008: cgt.un
L_000a: ldc.i4.0
L_000b: ceq
L_000d: stloc.1
L_000e: ldloc.1
L_000f: brtrue.s L_0021
L_0012: ldarg.1
L_0013: isinst string
L_0018: stloc.0
L_0019: ldarg.0
L_0021: ret
为了简短起见,已对这些进行了编辑(删除了方法声明、nops 和对某事()的调用)。
【问题讨论】:
-
我不会称之为典型用例,更典型的是 Bar y = x as Bar; if (y != null) { do_stuff(); }。如果您仍然使用 as,为什么要先检查 is?
-
老实说,第一个给我的感觉好像是由一个不知道
as做什么的人写的。 -
是的,但那是因为你知道 as 是做什么的 :)。如果你忘记了你所知道的,第一个对你来说不是更漂亮吗?
-
因为我刚刚更新了我的答案,第一个 sn-p 在面对多个线程时会受到竞争条件的影响,即 y 仍然可以为空。 as+null 检查版本不受这种竞争的影响。
-
+1 个不错的问题标题 :-)
标签: c# .net as-keyword