Is 与使用 null 检查的 try 强制转换

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

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

变成这样:

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

为什么会有这样的变化?我习惯于 Resharper 建议优化变更和代码缩减变更,但这感觉像是它想把我的单个语句变成一个两行程序。

根据 MSDN:

如果满足以下两个条件,则 表情的计算结果为 true 满足以下条件:

表达式 不为空。表达式可以强制转换为 类型 形式 (type)(expression)的强制转换表达式将在没有 抛出一个例外。

是我理解错了,还是 is没有做完全相同的检查,只是在一行中没有显式地创建另一个局部变量来进行 null 检查?

65567 次浏览

它还应该提出第二个变化:

(MyType)myObj.myProp

进入

myObjRef

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

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.

例如:

myObjRef.SomeProperty

比这更好:

((MyType)myObj.myProp).SomeProperty

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

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

对我来说,这似乎取决于它是否属于那种类型的可能性。如果对象在大多数情况下都属于这种类型,那么事先进行强制转换肯定会更有效率。如果它只是偶尔的那种类型,那么它可能是更理想的首先检查是。

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

对我来说,可读性和范围通常是更重要的因素。我不同意 ReSharper 的观点,并且仅仅出于这个原因使用“ is”操作符; 如果这是一个真正的瓶颈,以后再进行优化。

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

清醒警告:

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

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

目前还没有关于腰带以下到底发生了什么的信息,看看这个例子:

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

但是请注意,这并不一定转化为一个更快的结果,因为我们可以看到 给你。自从问了这个问题之后,情况似乎有所改善: 施法速度似乎和以前一样快,但是 aslinq现在快了大约3倍。

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

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