'是'与尝试投与空检查

我注意到,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 

这里重要的是isinstcastclass调用 – 都相对昂贵。 如果你比较这个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