'is' versus try cast with null check
我注意到Resharper建议我把这个:
1 2 3 4 |
进入这个:
1 2 3 4 5 | var myObjRef = myObj.myProp as MyType; if (myObjRef != null) { ... } |
为什么会建议这种改变?我习惯于重新分析建议优化更改和代码缩减更改,但这感觉像是它想采用我的单个语句并将其转换为两行程序。
根据msdn:
An is expression evaluates to true if both of the following conditions
are met:expression is not null. expression can be cast to type. That is, a
cast expression of the form(type)(expression) will complete without
throwing an exception.
我是否误解了这一点,或者不是
因为只有一个演员。比较一下:
1 2 3 4 5 6 | 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 ... } |
对此:
1 2 3 4 5 6 | var myObjRef = myObj.myProp as MyType; // only one cast if (myObjRef != null) { // myObjRef is already MyType and doesn't need to be cast again ... } |
C 7.0支持使用模式匹配的更紧凑的语法:
1 2 3 4 |
最好的选择是使用这样的模式匹配:
1 2 3 4 5 | if (value is MyType casted){ //Code with casted as MyType //value is still the same } //Note: casted can be used outside (after) the 'if' scope, too |
目前还没有关于皮带下面实际发生的情况的信息。看看这个例子:
1 2 3 4 5 |
这转化为以下IL:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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 |
这里最重要的是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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 |
值得一提的是,值类型将使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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 |
但是请注意,正如我们在这里看到的,这并不一定意味着会产生更快的结果。不过,自问到这个问题以来,似乎已经有了一些改进:演员阵容的执行速度似乎和以前一样快,但
重新竖琴警告:
两者都可以工作,这取决于您的代码如何更适合您。在我的例子中,我只是忽略了那个警告:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | //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(); |
在我的代码中,第二种方法的性能越来越长,越来越差。
对我来说,这似乎取决于它是否会是那种类型的可能性。如果对象大部分时间都是这种类型,那么做前向投影肯定会更有效。如果只是偶尔出现这种情况,那么最好先检查一下IS。
与类型检查的成本相比,创建局部变量的成本可以忽略不计。
可读性和范围通常是我更重要的因素。我不同意Resharper,因此只使用"is"操作符;如果这是一个真正的瓶颈,请稍后进行优化。
(我假设您在这个函数中只使用一次
我想说这是为了制作一个强类型的myobj.myprop版本,它是myobjref。然后,当您在块中引用此值时,应该使用此选项,而不必执行强制转换。
例如,这:
1 | myObjRef.SomeProperty |
比这更好:
1 | ((MyType)myObj.myProp).SomeProperty |
它还应该建议进行第二次更改:
1 | (MyType)myObj.myProp |
进入之内
1 | myObjRef |
与原始代码相比,这将保存属性访问和强制转换。但只有在将