我注意到Resharper建议我转过身来:
if (myObj.myProp is MyType)
{
...
}
进入这个:
var myObjRef = myObj.myProp as MyType;
if (myObjRef != null)
{
...
}
为什么会出现这种变化?我已经习惯了Resharper建议优化更改和代码减少更改,但这感觉就像它想要我的单一陈述并将其变成双线。
根据MSDN:
如果同时满足以下两个条件, 表达式的计算结果为true 满足:
表达式不为空。表达式可以转换为 type 。这是一个 {@ 1}}形式的强制表达式将在没有的情况下完成 抛出异常。
我是否误读了,或者(type)(expression)
只进行了一次完全相同的检查,而不需要为空检查显式创建另一个局部变量?
答案 0 :(得分:123)
因为只有一个演员。比较一下:
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
...
}
C#7.0使用pattern matching支持更紧凑的语法:
if (myObj.myProp is MyType myObjRef)
{
...
}
答案 1 :(得分:7)
最好的选择是使用模式匹配:
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
答案 2 :(得分:6)
目前还没有关于腰带以下实际发生情况的信息。看一下这个例子:
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
来电 - 两者都相对昂贵。如果将其与替代方案进行比较,您可以看到它只进行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
另外值得一提的是,值类型将使用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
但请注意,这并不一定会转化为更快的结果,因为我们可以看到here。自从问到这个问题后,似乎有了一些改进:演员表现得像以前一样快,但as
和linq
现在大约快了3倍。
答案 3 :(得分:4)
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();
在我的代码中,第二种方式的性能越来越差。
答案 4 :(得分:3)
对我而言,这似乎取决于它是否属于这种类型的可能性。如果对象在大多数时间属于那种类型,那么在前面进行渲染肯定会更有效。如果只是偶尔使用那种类型,那么先用is检查可能更为理想。
与类型检查的成本相比,创建局部变量的成本非常微不足道。
可读性和范围对我来说通常是更重要的因素。我不同意ReSharper,仅仅因为这个原因使用“is”运算符;如果这是一个真正的瓶颈,那么稍后进行优化。
(我假设你在这个函数中只使用myObj.myProp is MyType
一次)
答案 5 :(得分:0)
也应该建议第二次改变:
(MyType)myObj.myProp
进入
myObjRef
与原始代码相比,这可以保存属性访问和强制转换。但只有在将is
更改为as
后才能实现。
答案 6 :(得分:0)
我想这是制作myObj.myProp的强类型版本,即myObjRef。当你在块中引用这个值时,应该使用它,而不是必须进行转换。
例如,这个:
myObjRef.SomeProperty
比这更好:
((MyType)myObj.myProp).SomeProperty