2009-06-05 14 views
8

私はこのキーワードの機能を知っていますが、どのように低いレベルで動作するか知りたいと思います。"as"キーワードはどのように内部的に動作しますか?

どちらが速いのですか?そして、彼らはいつも同じ結果を出しますか?もしそうなら、なぜ2つの異なる方法がありますか?

// Is there an overhead? An internal try catch? 
Class123 obj = someobject as Class123; 

if (Class123 != null) 
{ 
    //OK 
} 

または

Class123 obj = null; 

if (someobject is Class123) 
{ 
    obj = (Class123)someobject; 
} 
+1

答えを見る私は "ある"と思うのは、あるオブジェクトが型かどうかを知ることだけで、キャストだけでなく何らかのアクションをとることです。 例:if(objがClassManager){Write( "あなたはマネージャー")} –

答えて

16

asキーワードを使用した場合には内部のtry-catchが起こっていないあります。私が知る限り、コンパイラ/ CLRに機能が組み込まれているので、型チェックは暗黙的かつ自動化されています。

シンプルなルール:あなたは常にオブジェクトが知られているタイプを持っている(したがって、それが間違った型の偶然であれば有用なエラーを受け取る)ことを期待とき
は直接キャストを使用してください。オブジェクトがの場合、常に既知のタイプのである場合はasキーワードを使用してください。

asキーワードが存在する理由は、純粋にプログラマーの便宜のためです(試し読みが遅くなることを示唆していますが)。ご指摘の通りあなたは、のような手動でそれを自分で実装することができます:

var castObj = (obj is NewType) ? (NewType)obj : null; 

これは、キーワードは簡潔の目的のために主に存在している「として」事実を強調しています。

ここで、2つのパフォーマンスの差はごくわずかです。 asキーワードは、型チェックのためにおそらくわずかに遅くなりますが、大部分の状況でコードに影響を及ぼす可能性は低いです。言うまでもなく、時期尚早の最適化は決して賢明なことではありません。あなたが本当に望むならベンチマークですが、あなたの状況に対してより便利/適切である方法を使用することを推奨し、パフォーマンスについて全く心配する必要はありません。

+0

私はnullを考えていませんでしたが、これは重要ですが、オブジェクトがコンバーチブルでない場合は "try"悪い英語のため) –

+4

確かに "as"とキャストはヌルの存在下で同じように動作しますか?オブジェクトが他の型であることを決して期待していないときは直接キャストを使用してはいけません。 – Grokys

+1

asは少し遅いかもしれませんが、if(xはT)(T)x.Foo()でtypecheckが2回必要です。おそらくx == nulより少し遅いです。 –

5

これは、+ castがタイプを2回確認する必要がある間だけ、タイプを1回ckeckする必要があるためです。 MSDN: as (C# Reference)によると

+0

これが正しいかどうかわかりません。 "as"というキーワードは、私が知っている限り、まったく同じことを意味します。 – Noldorin

+0

この説明はパフォーマンスルールとしてFxCopにも記載されています。だから私はそれが正しいと思います。 – Scoregraphic

22

ザ・オペレータは、キャスト操作のようなものであるとして。ただし、変換が不可能な場合は、例外が発生する代わりにnullが返されます。次の式を考えてみましょう:

expression as type 

発現が一度だけ評価さ除いそれは次の式と同等です。 (オペランドとして

expression is type ? (type)expression : (type)null 

第一の変形...

string str1 = strAsObject as string; 
if (str1 != null) 
{ 
    this.blabla(str1); 
} 

...このILコードにコンパイルされます。

L_0009: ldloc.1 
L_000a: isinst string 
L_000f: stloc.2 
L_0010: ldloc.2 
L_0011: ldnull 
L_0012: ceq 
L_0014: stloc.s CS$4$0000 
L_0016: ldloc.s CS$4$0000 
L_0018: brtrue.s L_0024 
L_001a: nop 
L_001b: ldarg.0 
L_001c: ldloc.2 
L_001d: call instance void TestWinFormsApplication001.Form1::blabla(string) 
L_0022: nop 
L_0023: nop 

...と第二の変形... ...

if (strAsObject is string) 
{ 
    string str2 = (string) strAsObject; 
    this.blabla(str2); 
} 

はオペランド+キャストである)このILコードにコンパイルされます。

L_0024: ldloc.1 
L_0025: isinst string 
L_002a: ldnull 
L_002b: cgt.un 
L_002d: ldc.i4.0 
L_002e: ceq 
L_0030: stloc.s CS$4$0000 
L_0032: ldloc.s CS$4$0000 
L_0034: brtrue.s L_0047 
L_0036: nop 
L_0037: ldloc.1 
L_0038: castclass string 
L_003d: stloc.3 
L_003e: ldarg.0 
L_003f: ldloc.3 
L_0040: call instance void TestWinFormsApplication001.Form1::blabla(string) 
L_0045: nop 
L_0046: nop 

...ですので、唯一の違いはcastclassコードL_0038のコードです。いくつかストレートに設定するには

9

:あなたは、オブジェクトがあなたにキャストしている型であることを確認しているとき

型キャストを行うべきです。 nullにすることができます(この場合、キャストする値の型でない限りnullが返されます)

わからない場合は、「as」演算子を使用できます。オブジェクトがキャスト可能でないか、またはオブジェクトがヌルの場合、nullが返されます。それは、ランタイムに組み込まれているので、型キャストは、castclass IL声明に変換しながら、オペレータは、専用のIL声明( isinst )に変換「と」

。コンパイラは正しいIL文を発行するだけです。

+0

ええ、これは確かに正しいです。あなたがここでの説明のために多くの功績を残しておかなければならないので、投票されました。 – Noldorin

+0

新しいものを作成するのではなく、あなたの答えを編集しましたが、まだそれをすることは許されません:-) –

5

この質問はすでによく答えられていますが、これまでは難しい数字がありませんでした。

Over 100000000 iterations 
AS : Failure 00:00:00.9282403 
Cast : Failure 00:00:00.9868966 
AS : Success 00:00:00.9350227 
Cast : Success 00:00:01.1382759 

私は唯一の結論は、これらの図から取ることを指摘したい

数字は一貫これらの比率に戻ってくるには、性能の観点から、得られることが非常に少ないがあるということですこれらの方法のうちの1つを他のものよりも選択することにより、。 1回のコールではほとんど違いがありません(ゼロになる傾向はほとんどありません)。つまり、 "as"は速くなります:)

その後、上記の数字は主に理由があります。

"As"は失敗時に成功するよりも時間がかかります。成功すると何も起こらず、その値をそのまま使用するか、単純にコピーすることができます。失敗した場合は、ヌル参照をコピーするためにジャンプが必要です。

"キャスト"が失敗すると、 "キャスト"が早くなり、もう一度やり直すことはありません。成功すれば、それははるかに遅く、それは "is"への呼び出しのオーバーヘッドを持ち、その後キャストされます。私が失敗した場合にキャストが長く失敗ASよりも時間がかかることを驚いているが

編集

要求されたとして、try/catchブロック

Over 100000000 iterations 
Catch : Failure 05.05:00:00 // approximately, because I didn't hang around 
Catch : Success 00:00:01.4000952 

コードでキャストの数字第1の組を生成したもの

class Program 
{ 
    const int ITERATION_COUNT = 100000000; 
    private static UInt64 stringCount = 0; 
    private static UInt64 objectCount = 0; 
    static void Main(string[] args) 
    { 
     Console.WriteLine("Over {0} iterations ", ITERATION_COUNT); 

     string s = "Hello"; 
     object o = new Int32(); 

     RunTest("AS : Failure {0}", TestAs, o); 
     RunTest("Cast : Failure {0}", TestIs_And_Cast, o); 
     RunTest("AS : Success {0}", TestAs, s); 
     RunTest("Cast : Success {0}", TestIs_And_Cast, s); 

     Console.WriteLine("Press any key to stop"); 
     Console.ReadKey(); 

    } 
    private static void RunTest(string testDescription, Action<object> testToRun, object arg) 
    { 
     Stopwatch sw = new Stopwatch(); 
     sw.Start(); 
     for (int i = 0; i < ITERATION_COUNT; i++) 
      testToRun(arg); 
     sw.Stop(); 
     Console.WriteLine(testDescription, sw.Elapsed); 
    } 
    static void TestAs(object obj) 
    { 
     string s = obj as string; 
     if (s != null) 
      stringCount++; 
     else 
      objectCount++; 
    } 
    static void TestIs_And_Cast(object obj) 
    { 
     string s = null; 
     if (obj is string) 
     { 
      s = (string)obj; 
      stringCount++; 
     } 
     else 
      objectCount++; 
    } 
} 
+2

"as"は "is"より先にキャストされています。私は、パフォーマンスへの影響はごくわずかであることに同意します。おそらく、例外処理を伴うキャストを使ってテストケースを追加することもできます。うまくいけば、それは、この種のものの例外をキャッチすることが「完了していない」ことを一部の人々に認識させることになります。 :) –

+0

したがって、ミリ秒ごとにこれらのどれか(asまたはcast)を実行できますか? - > 100,000,000/1000 = 100,000のいずれかで約1秒で1億回の小切手があったことを考えると、したがって、100,000単位またはキャスト単位のチェックをミリ秒単位で*行うことができます。または、各チェック(いずれかの、おおよそ)は1秒の100万分の1を要します。 –

関連する問題