首页 文章

'is'与使用空检查的try cast相比

提问于
浏览
95

我注意到Resharper建议我转过身:

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

进入这个:

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

为什么会建议这种变化?我已经习惯了Resharper建议优化更改和代码减少更改,但这感觉就像它想要我的单一陈述并将其转换为双线程 .

根据MSDN

如果满足以下两个条件,则表达式求值为true:expression不为null . 表达式可以转换为类型 . 也就是说,形式(类型)(表达式)的强制转换表达式将在不抛出异常的情况下完成 .

我是误读了,或者不是 is 只是在一行中进行完全相同的检查,而不需要为空检查显式创建另一个局部变量?

7 回答

  • 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
        ...
    }
    

    对此:

    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)
    {
        ...
    }
    
  • 0

    目前还没有关于皮带下面究竟发生了什么的信息 . 看看这个例子:

    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 电话 - 两者都相对昂贵 . 如果将其与替代方案进行比较,您可以看到它只进行 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 . 自从问到这个问题后,似乎有了一些改进:演员似乎表现得和以前一样快,但是 aslinq 现在快了大约3倍 .

  • 122

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

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

    在我的代码第二种方式是更长和更差的性能 .

  • 0

    对我来说,这似乎取决于它将成为那种类型的可能性 . 如果对象在大多数时间属于那种类型,那么在前面进行渲染肯定会更有效 . 如果只是偶尔使用那种类型,那么先用is检查可能更为理想 .

    与类型检查的成本相比,创建局部变量的成本非常微不足道 .

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

    (我假设你在这个函数中只使用 myObj.myProp is MyType 一次)

  • 3

    它应该建议第二个变化:

    (MyType)myObj.myProp
    

    myObjRef
    

    与原始代码相比,这可以保存属性访问和转换 . 但是只有在将 is 更改为 as 之后才有可能 .

  • 6

    我想这是制作myObj.myProp的强类型版本,即myObjRef . 然后,当您在块中引用此值时,应该使用此方法,而不必进行强制转换 .

    例如,这个:

    myObjRef.SomeProperty
    

    比这更好:

    ((MyType)myObj.myProp).SomeProperty
    

相关问题