'是'与尝试投与空检查
我注意到,Resharperbuild议我这样做:
if (myObj.myProp is MyType) { ... }
进入这个:
var myObjRef = myObj.myProp as MyType; if (myObjRef != null) { ... }
为什么会提出这种改变? 我已经习惯了Resharper提出的优化改变和代码缩减的改变,但是这感觉就像是想要把我的单一语句转换成双线程。
根据MSDN :
如果满足以下两个条件, 则 expression式的计算结果为true:
expression式不为空。 expression式可以转换为types 。 也就是说,表单
(type)(expression)
强制转换(type)(expression)
将会完成而不会引发exception。
我误读了,还是不是做了完全相同的检查,只是在一行中,而不需要显式创build另一个局部variables的空检查?
因为只有一个演员 比较一下:
if (myObj.myProp is MyType) // cast #1 { var myObjRef = (MyType)myObj.myProp; // needs to be cast a second time // before using it as a MyType ... }
对此:
var myObjRef = myObj.myProp as MyType; // only one cast if (myObjRef != null) { // myObjRef is already MyType and doesn't need to be cast again ... }
对我来说,这似乎取决于是否会有这种types的可能性。 如果大部分时间对象属于这种types的话,那么最好先做一个更高效的工作。 如果只是偶尔有这种types的话,那么最好先用is来检查。
与types检查的成本相比,创build局部variables的成本是微不足道的。
可读性和范围对于我来说通常是更重要的因素。 我会不同意ReSharper,仅仅因为这个原因就使用“is”操作符。 如果这是一个真正的瓶颈,以后优化。
(我假设你只在这个函数中使用myObj.myProp is MyType
)
Resharper警告:
"Type check and direct cast can be replaced with try cast and check for null"
两者都可以工作,这取决于你的代码更适合你。 就我而言,我只是忽略了这个警告:
//1st way is n+1 times of casting if (x is A) ((A)x).Run(); else if (x is B) ((B)x).Run(); else if (x is C) ((C)x).Run(); else if (x is D) ((D)x).Run(); //... else if (x is N) ((N)x).Run(); //... else if (x is Z) ((Z)x).Run(); //2nd way is z times of casting var a = x as Type A; var b = x as Type B; var c = x as Type C; //.. var n = x as Type N; //.. var z = x as Type Z; if (a != null) a.Run(); elseif (b != null) b.Run(); elseif (c != null) c.Run(); ... elseif (n != null) n.Run(); ... elseif (x != null) x.Run();
在我的代码中,第二种方式是更长和更差的性能。
目前还没有关于皮带下面发生的事情的信息。 看看这个例子:
object o = "test"; if (o is string) { var x = (string) o; }
这转换为以下IL:
IL_0000: nop IL_0001: ldstr "test" IL_0006: stloc.0 // o IL_0007: ldloc.0 // o IL_0008: isinst System.String IL_000D: ldnull IL_000E: cgt.un IL_0010: stloc.1 IL_0011: ldloc.1 IL_0012: brfalse.s IL_001D IL_0014: nop IL_0015: ldloc.0 // o IL_0016: castclass System.String IL_001B: stloc.2 // x IL_001C: nop IL_001D: ret
这里重要的是isinst
和castclass
调用 – 都相对昂贵。 如果你比较这个select,你可以看到它只是一个isinst
检查:
object o = "test"; var oAsString = o as string; if (oAsString != null) { } IL_0000: nop IL_0001: ldstr "test" IL_0006: stloc.0 // o IL_0007: ldloc.0 // o IL_0008: isinst System.String IL_000D: stloc.1 // oAsString IL_000E: ldloc.1 // oAsString IL_000F: ldnull IL_0010: cgt.un IL_0012: stloc.2 IL_0013: ldloc.2 IL_0014: brfalse.s IL_0018 IL_0016: nop IL_0017: nop IL_0018: ret
另外值得一提的是,一个值types将使用unbox.any
而不是castclass
:
object o = 5; if (o is int) { var x = (int)o; } IL_0000: nop IL_0001: ldc.i4.5 IL_0002: box System.Int32 IL_0007: stloc.0 // o IL_0008: ldloc.0 // o IL_0009: isinst System.Int32 IL_000E: ldnull IL_000F: cgt.un IL_0011: stloc.1 IL_0012: ldloc.1 IL_0013: brfalse.s IL_001E IL_0015: nop IL_0016: ldloc.0 // o IL_0017: unbox.any System.Int32 IL_001C: stloc.2 // x IL_001D: nop IL_001E: ret
请注意,这不一定意味着更快的结果,我们可以在这里看到。 自从这个问题被问到以后似乎有所改善:演员似乎执行速度与以前一样快,但现在和linq
现在快了大约3倍。
最好的select是使用像这样的模式匹配:
if (value is MyType casted){ //Code with casted as MyType //value is still the same }
它应该提出第二个改变:
(MyType)myObj.myProp
成
myObjRef
与原始代码相比,这可以节省一个属性访问权限和一个强制转换。 但只有改变后才可以。
我会说这是做一个强types的myObj.myProp,这是myObjRef。 这应该在块中引用该值时使用,而不是必须进行强制转换。
例如,这个:
myObjRef.SomeProperty
比这更好:
((MyType)myObj.myProp).SomeProperty