'is' 与带有 null 检查的尝试转换

问题描述 投票:0回答:8

我注意到 Resharper 建议我转动这个:

if (myObj.myProp is MyType)
{
   ...
}

进入这个:

var myObjRef = myObj.myProp as MyType;
if (myObjRef != null)
{
   ...
}

为什么会建议进行此更改?我习惯了 Resharper 建议优化更改和代码减少更改,但这感觉就像它想要将我的单个语句变成两行语句。

根据MSDN

如果满足以下两个条件,

is 表达式 的计算结果为 true 满足:

表达式不为空。表达式可以转换为type。这是一个

(type)(expression)
形式的强制转换表达式将完成,无需 抛出异常。

我是否误读了,或者

is
没有做完全相同的检查,只是在一行中而不需要为空检查显式创建另一个局部变量?

c# .net casting resharper
8个回答
179
投票

因为只有一个演员。比较一下:

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 使用 模式匹配支持更紧凑的语法:

if (myObj.myProp is MyType myObjRef)
{
    ...
}

14
投票

最好的选择是使用这样的模式匹配:

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

9
投票

目前还没有关于腰带以下实际发生的情况的信息。看看这个例子:

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   

但请注意,这并不一定会转化为更快的结果,正如我们在此处所看到的那样。不过,自从提出这个问题以来,似乎已经有了一些改进:转换似乎执行得和以前一样快,但是

as
linq
现在大约快了 3 倍。


4
投票

重新磨刀警告:

"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();

在我的代码中,第二种方式更长且性能更差。


3
投票

对我来说,这似乎取决于它是否属于这种类型的可能性。如果对象大多数时候都是该类型,那么预先进行转换肯定会更有效。如果只是偶尔出现这种类型,那么首先使用 is 检查可能会更理想。

与类型检查的成本相比,创建局部变量的成本可以忽略不计。

可读性和范围对我来说通常是更重要的因素。我不同意 ReSharper,仅出于这个原因就使用“is”运算符;如果这是真正的瓶颈,请稍后优化。

(我假设您在此函数中仅使用

myObj.myProp is MyType
一次)


1
投票

我想说这是为了制作 myObj.myProp 的强类型版本,即 myObjRef。当您在块中引用该值时,应该使用它,而不是必须进行强制转换。

例如这个:

myObjRef.SomeProperty

比这个更好:

((MyType)myObj.myProp).SomeProperty

0
投票

它也应该建议第二次改变:

(MyType)myObj.myProp

进入

myObjRef

与原始代码相比,这节省了属性访问和强制转换。但只有将

is
更改为
as
后才有可能。


0
投票

c#11:

if (myObj is not MyType element || element?.myProp == null) { return;}
© www.soinside.com 2019 - 2024. All rights reserved.