C# 'is' 与带空检查的 try cast

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13405714/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 08:26:38  来源:igfitidea点击:

'is' versus try cast with null check

c#.netcastingresharper

提问by HotN

I noticed that Resharper suggests that I turn this:

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

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

into this:

进入这个:

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

Why would it suggest this change? I'm used to Resharper suggesting optimization changes and code reduction changes, but this feels like it wants to take my single statement and turn it into a two-liner.

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

According to MSDN:

根据MSDN

An isexpressionevaluates to true if both of the following conditions are met:

expressionis not null. expression can be cast to type. That is, a cast expression of the form (type)(expression)will complete without throwing an exception.

一个表达式的计算结果为真,如果同时满足以下两个条件都满足:

表达式不为空。表达式可以强制转换为type。也就是说,表单的强制转换表达式(type)(expression)将完成而不会引发异常。

Am I misreading that, or doesn't isdo the exact same checks, just in a single line without the need to explicitly create another local variable for the null check?

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

采纳答案by Jeff E

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:

C# 7.0 支持使用模式匹配的更紧凑的语法:

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

回答by Ben Voigt

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 isto as.

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

回答by Jerad Rose

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.

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

For example, this:

例如,这个:

myObjRef.SomeProperty

is better than this:

比这更好:

((MyType)myObj.myProp).SomeProperty

回答by Derrick

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.

对我来说,这似乎取决于它是否属于那种类型的可能性有多大。如果对象大部分时间都是这种类型,那么预先进行转换肯定会更有效。如果只是偶尔出现这种类型,那么首先检查 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.

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

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

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

回答by Tom

Resharper warning:

Resharper警告:

"Type check and direct cast can be replaced with try cast and check for null"

"Type check and direct cast can be replaced with try cast and check for null"

Both will work, it depends how your code suits you more. In my case I just ignore that warning:

两者都可以,这取决于您的代码如何更适合您。就我而言,我只是忽略该警告:

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

In my code 2nd way is longer and worse performance.

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

回答by Jeroen Vannevel

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:

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 isinstand castclasscalls -- both relatively expensive. If you compare that to the alternative you can see it only does an isinstcheck:

这里重要的是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  

Also worth mentioning is that a value type will use unbox.anyrather than castclass:

另外值得一提的是,值类型将使用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   

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 asand linqare now approximately 3 times faster.

但是请注意,这并不一定会转化为更快的结果,正如我们在此处看到的那样。目前似乎已经改善,因为这个问题被问,但:铸件看起来就像他们曾经是,但要快速执行aslinq现在大约快3倍。

回答by Francesco Cattoni

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