'is' versus try cast with null check

后端 未结 7 604
情歌与酒
情歌与酒 2020-12-08 03:28

I noticed that Resharper suggests that I turn this:

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

into this:

var myObjRef = myObj.         


        
相关标签:
7条回答
  • 2020-12-08 03:58

    It should be suggesting a second change as well:

    (MyType)myObj.myProp
    

    into

    myObjRef
    

    This saves a property access and a cast, compared to the original code. But it's only possible after changing is to as.

    0 讨论(0)
  • 2020-12-08 04:09

    I would say this is to make a strongly-typed version of myObj.myProp, which is myObjRef. This should then be used when you are referencing this value in the block, vs. having to do a cast.

    For example, this:

    myObjRef.SomeProperty
    

    is better than this:

    ((MyType)myObj.myProp).SomeProperty
    
    0 讨论(0)
  • 2020-12-08 04:12

    Because there's only one cast. Compare this:

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

    to this:

    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 supports a more compact syntax using pattern matching:

    if (myObj.myProp is MyType myObjRef)
    {
        ...
    }
    
    0 讨论(0)
  • 2020-12-08 04:15

    To me this seems dependent on what the odds are that it's going to be of that type or not. It would certainly be more efficient to do the cast up front if the object is of that type most of the time. If it's only occasionally of that type then it may be more optimal to check first with is.

    The cost of creating a local variable is very negligible compared to the cost of the type check.

    Readability and scope are the more important factors for me typically. I would disagree with ReSharper, and use the "is" operator for that reason alone; optimize later if this is a true bottleneck.

    (I'm assuming that you are only using myObj.myProp is MyType once in this function)

    0 讨论(0)
  • 2020-12-08 04:17

    There's no information yet about what actually happens below the belt. Take a look at this example:

    object o = "test";
    if (o is string)
    {
        var x = (string) o;
    }
    

    This translates to the following 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   
    

    What matters here are the isinst and castclass calls -- both relatively expensive. If you compare that to the alternative you can see it only does an isinst check:

    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  
    

    Also worth mentioning is that a value type will use unbox.any rather than 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   
    

    Note however that this not necessarily translates to a faster result as we can see here. There seem to have been improvements since that question was asked though: casts seem to be performed as fast as they used to be but as and linq are now approximately 3 times faster.

    0 讨论(0)
  • 2020-12-08 04:18

    The best option is use pattern matching like that:

    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
    
    0 讨论(0)
提交回复
热议问题