web-dev-qa-db-ja.com

'is'対nullチェックでキャストしよう

Resharperはこれを回すことを提案していることに気付きました。

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

これに:

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

なぜこの変更を提案するのですか?私はResharperが最適化の変更とコード削減の変更を提案するのに慣れていますが、これは私の1つのステートメントを2ライナーに変えたいと思っているように感じます。

[〜#〜] msdn [〜#〜] によると:

isexpressionは、以下の両方の条件が満たされた場合にtrueと評価されます。

expressionはnullではありません。式はtypeにキャストできます。つまり、(type)(expression)という形式のキャスト式は、例外をスローすることなく完了します。

Nullチェック用に別のローカル変数を明示的に作成する必要なしに、1行だけでisがまったく同じチェックをしないのですか、それとも誤解していますか?

100
HotN

キャストが1つしかないためです。これを比較してください:

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

最適なオプションは、次のようなパターンマッチングを使用することです。

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
8

実際にベルトの下で何が起こるかについての情報はまだありません。この例を見てください:

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倍高速になりました。

6
Jeroen Vannevel

再シャーパー警告:

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

私のコードの2番目の方法では、パフォーマンスは長くなり、悪化します。

4
Tom

私には、これはそのタイプになるかどうかという可能性に依存しているようです。オブジェクトがほとんど常にそのタイプである場合、前もってキャストを行う方が確実に効率的です。たまにしかそのタイプではない場合は、最初にチェックするのがより最適かもしれません。

ローカル変数を作成するコストは、型チェックのコストと比較して非常にわずかです。

通常、読みやすさと範囲は私にとってより重要な要素です。私はReSharperに反対し、その理由だけで「is」演算子を使用します。これが真のボトルネックである場合は、後で最適化します。

(この関数でmyObj.myProp is MyTypeを1回だけ使用していると仮定しています)

3
Derrick

2番目の変更も提案する必要があります。

(MyType)myObj.myProp

myObjRef

これにより、元のコードと比較して、プロパティへのアクセスとキャストが節約されます。ただし、isasに変更した後にのみ可能です。

0
Ben Voigt

これは、myObjRefであるmyObj.myPropの厳密に型指定されたバージョンを作成することです。これは、ブロックでこの値を参照しているときと、キャストを行う必要があるときに使用する必要があります。

たとえば、これ:

myObjRef.SomeProperty

これよりも優れています:

((MyType)myObj.myProp).SomeProperty
0
Jerad Rose