0

次のクラスがあります:

public class A
{
    public int MyProperty { get; set; }
}

そして、メインの次のコード:

object obj = new A();

Stopwatch sw = Stopwatch.StartNew();
var res = obj as A;
if (res != null)
{
    res.MyProperty = 10;
    Console.WriteLine("obj is A (as)" + sw.Elapsed);
}
sw.Stop();

Stopwatch sw2 = Stopwatch.StartNew();
if (obj.GetType() == typeof(A))
{
    A a = (A)obj;
    a.MyProperty = 10;
    Console.WriteLine("obj is A (GetType)" + sw2.Elapsed);
}
sw2.Stop();

Stopwatch sw3 = Stopwatch.StartNew();
var isA = obj is A;
if (isA)
{
    A a = (A)obj;
    a.MyProperty = 19;
    Console.WriteLine("obj is A (is)" + sw3.Elapsed);
}
sw3.Stop();

Console.ReadKey();

結果は次のとおりです。

obj is A (as) 00:00:00.0000589
obj is A (GetType) 00:00:00.0000024
obj is A (is) 00:00:00.0000006

重要なのは、演算子'is'は常に'as'よりも高速に動作するということです。なぜ「as」は「is」よりも遅いのですか?GetType()でさえ'as'よりも高速です。'is'およびGetType()と比較してこのような遅延を引き起こす'as'演算子を表すもの。

4

3 に答える 3

4

コンソールへのストリームを開かなければならないのはこれが最初だと思いConsole.Writeます。そのため、かなり時間がかかります。

とにかく、コンソールへの書き込みはキャストを行うよりもはるかに時間がかかるため、テストからキャストについて結論を出すことはできません。

キャストを10億回実行し、キャストごとにコンソールに何も書き込まないことで、より合理的な結果が得られます。

object obj = new A();
int iterations = 1000000000;

Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++) {
  var res = obj as A;
  if (res != null) {
    res.MyProperty = 10;
  }
}
sw.Stop();
Console.WriteLine("obj is A (as)" + sw.Elapsed);

Stopwatch sw2 = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++) {
  if (obj.GetType() == typeof(A)) {
    A a = (A)obj;
    a.MyProperty = 10;
  }
}
sw2.Stop();
Console.WriteLine("obj is A (GetType)" + sw2.Elapsed);

Stopwatch sw3 = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++) {
  var isA = obj is A;
  if (isA) {
    A a = (A)obj;
    a.MyProperty = 19;
  }
}
sw3.Stop();
Console.WriteLine("obj is A (is)" + sw3.Elapsed);

出力例:

obj is A (as)00:00:00.3937249
obj is A (GetType)00:00:00.3452988
obj is A (is)00:00:01.0193541
于 2013-02-25T10:08:16.330 に答える
2

「as」が「is」よりも遅いことを示している場合、測定値は正しくありません。

これが当てはまらない理由は、「as」キーワードと「is」キーワードの両方がisinstIL命令を生成しますが、「is」命令は戻り値の追加チェックを生成するためです。

リフレクターを使用して生成されたILコードを調べることができるため、これを決定するためにタイミングを実行する必要はありません。

たとえば、このメソッドは次のとおりです。

static bool isTest(object value)
{
    return value is Random;
}

このILを生成します:

.method private hidebysig static bool isTest(object 'value') cil managed
{
    .maxstack 8
    L_0000: ldarg.0 
    L_0001: isinst [mscorlib]System.Random
    L_0006: ldnull 
    L_0007: cgt.un 
    L_0009: ret 
}

この間:

static object asTest(object value)
{
    return value as Random;
}

生成:

.method private hidebysig static object asTest(object 'value') cil managed
{
    .maxstack 8
    L_0000: ldarg.0 
    L_0001: isinst [mscorlib]System.Random
    L_0006: ret 
}

「is」キーワードは、「as」キーワードと同じILに加えて、いくつかの追加の命令を生成します。したがって、この種の使用法では、「is」は「as」よりも遅くなければなりません。

于 2013-02-25T10:39:28.537 に答える
1

も設定しているせいかもしれませんMyPropertyので、初めてプロパティセッターをJITコンパイルします。

コメント行がある場合とない場合でこのコードを実行してみて、違いを確認してください。

    object obj = new A();

    // uncomment these lines and see the difference
    // A tmp = new A();
    // tmp.MyProperty = 100;

    Stopwatch sw = Stopwatch.StartNew();
    var res = obj as A;
    if (res != null) {
        res.MyProperty = 10;
    }
    sw.Stop();
    Console.WriteLine("as : " + sw.Elapsed);

    Stopwatch sw2 = Stopwatch.StartNew();
    if (obj.GetType() == typeof(A)) {
        A a = (A)obj;
        a.MyProperty = 10;
    }
    sw2.Stop();
    Console.WriteLine("GetType : " + sw2.Elapsed);

    Stopwatch sw3 = Stopwatch.StartNew();
    var isA = obj is A;
    if (isA) {
        A a = (A)obj;
        a.MyProperty = 19;
    }
    sw3.Stop();
    Console.WriteLine("is : " + sw3.Elapsed);
于 2013-02-25T10:10:41.980 に答える