adalah vs typeof

150

Manakah dari kode-kode ini yang lebih cepat?

if (obj is ClassA) {}

if (obj.GetType() == typeof(ClassA)) {}

Sunting: Saya sadar mereka tidak melakukan hal yang sama.

ilitirit
sumber
1
Menjawab pertanyaan serupa di sini: stackoverflow.com/questions/57701/…
swilliams

Jawaban:

167

Ini harus menjawab pertanyaan itu, dan kemudian beberapa.

Baris kedua,, if (obj.GetType() == typeof(ClassA)) {}lebih cepat, bagi mereka yang tidak ingin membaca artikel.

(Ketahuilah bahwa mereka tidak melakukan hal yang sama)

MagicKat
sumber
1
+1: Di masa lalu saya bertanya-tanya mengapa kompiler C # tidak dapat dikompilasi typeof(string).TypeHandledengan ldtokeninstruksi CIL, tetapi sepertinya CLR mengurusnya di JIT. Masih membutuhkan beberapa opcodes tambahan tetapi ini merupakan aplikasi optimisasi yang lebih umum.
Sam Harwell
2
Baca higherlogics.blogspot.ca/2013/09/... - mereka menguji ulang untuk kerangka kerja yang berbeda dan x86 vs x64 dengan hasil yang sangat berbeda.
CAD berbicara
1
Harap dicatat, ini hanya berlaku untuk jenis referensi. Dan perbedaan kecepatan tidak terlalu signifikan. Mengingat penalti tinju dalam hal jenis nilai untuk GetType, isselalu merupakan pilihan yang lebih aman sejauh menyangkut kinerja. Tentu saja mereka melakukan hal yang berbeda.
nawfal
Jika Anda memasukkannya ke dalam Resharper, menyarankan untuk mengubahnya menjadi "adalah"!
Rob Sedgwick
@nawfal, saya awalnya berpikir poin Anda tentang penalti tinju masuk akal untuk jenis struct, tetapi mengingat bahwa kami sedang menguji object obj;variabel, bukankah sudah boxed ketika ini cenderung diuji? Apakah ada kasus di mana Anda perlu menguji jenis sesuatu dan itu belum dikotakkan sebagai objek?
Rob Parker
193

Apakah penting yang lebih cepat, jika mereka tidak melakukan hal yang sama? Membandingkan kinerja pernyataan dengan makna berbeda sepertinya ide yang buruk.

ismemberi tahu Anda jika objek mengimplementasikan ClassAdi mana saja dalam jenis heirarki. GetType()memberi tahu Anda tentang jenis yang paling diturunkan.

Bukan hal yang sama.

Jay Bazuzi
sumber
7
Itu penting, karena dalam kasus saya, saya yakin mereka mengembalikan hasil yang sama.
ilitirit
37
@ [ilitirit]: mereka mengembalikan hasil yang sama sekarang, tetapi jika Anda menambahkan subkelas nanti mereka tidak akan
Steven A. Lowe
13
Mengoptimalkan sekarang akan membuat kode Anda rapuh dan sulit dipertahankan.
ICR
9
Kelas saya disegel.
ilitirit
26

Mereka tidak melakukan hal yang sama. Yang pertama berfungsi jika obj adalah tipe ClassA atau beberapa subclass dari ClassA. Yang kedua hanya akan cocok dengan objek bertipe ClassA. Yang kedua akan lebih cepat karena tidak harus memeriksa hierarki kelas.

Bagi mereka yang ingin tahu alasannya, tetapi tidak ingin membaca artikel yang dirujuk adalah vs typeof .

tvanfosson
sumber
1
@amitjha Saya sedikit khawatir karena tes itu dijalankan di bawah Mono sehingga tidak termasuk optimasi JIT yang dirujuk dalam artikel. Karena artikel tersebut menunjukkan yang sebaliknya, dalam pikiran saya pertanyaannya adalah pertanyaan terbuka. Dalam hal apa pun, membandingkan kinerja operasi yang melakukan berbagai hal tergantung pada jenisnya tampaknya merupakan latihan yang tidak berharga. Gunakan operasi yang cocok dengan perilaku yang Anda butuhkan, bukan yang "lebih cepat"
tvanfosson
16

Saya melakukan benchmarking di mana mereka melakukan jenis yang sama - disegel.

var c1 = "";
var c2 = typeof(string);
object oc1 = c1;
object oc2 = c2;

var s1 = 0;
var s2 = '.';
object os1 = s1;
object os2 = s2;

bool b = false;

Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < 10000000; i++)
{
    b = c1.GetType() == typeof(string); // ~60ms
    b = c1 is string; // ~60ms

    b = c2.GetType() == typeof(string); // ~60ms
    b = c2 is string; // ~50ms

    b = oc1.GetType() == typeof(string); // ~60ms
    b = oc1 is string; // ~68ms

    b = oc2.GetType() == typeof(string); // ~60ms
    b = oc2 is string; // ~64ms


    b = s1.GetType() == typeof(int); // ~130ms
    b = s1 is int; // ~50ms

    b = s2.GetType() == typeof(int); // ~140ms
    b = s2 is int; // ~50ms

    b = os1.GetType() == typeof(int); // ~60ms
    b = os1 is int; // ~74ms

    b = os2.GetType() == typeof(int); // ~60ms
    b = os2 is int; // ~68ms


    b = GetType1<string, string>(c1); // ~178ms
    b = GetType2<string, string>(c1); // ~94ms
    b = Is<string, string>(c1); // ~70ms

    b = GetType1<string, Type>(c2); // ~178ms
    b = GetType2<string, Type>(c2); // ~96ms
    b = Is<string, Type>(c2); // ~65ms

    b = GetType1<string, object>(oc1); // ~190ms
    b = Is<string, object>(oc1); // ~69ms

    b = GetType1<string, object>(oc2); // ~180ms
    b = Is<string, object>(oc2); // ~64ms


    b = GetType1<int, int>(s1); // ~230ms
    b = GetType2<int, int>(s1); // ~75ms
    b = Is<int, int>(s1); // ~136ms

    b = GetType1<int, char>(s2); // ~238ms
    b = GetType2<int, char>(s2); // ~69ms
    b = Is<int, char>(s2); // ~142ms

    b = GetType1<int, object>(os1); // ~178ms
    b = Is<int, object>(os1); // ~69ms

    b = GetType1<int, object>(os2); // ~178ms
    b = Is<int, object>(os2); // ~69ms
}

sw.Stop();
MessageBox.Show(sw.Elapsed.TotalMilliseconds.ToString());

Fungsi generik untuk menguji tipe generik:

static bool GetType1<S, T>(T t)
{
    return t.GetType() == typeof(S);
}
static bool GetType2<S, T>(T t)
{
    return typeof(T) == typeof(S);
}
static bool Is<S, T>(T t)
{
    return t is S;
}

Saya mencoba jenis kustom juga dan hasilnya konsisten:

var c1 = new Class1();
var c2 = new Class2();
object oc1 = c1;
object oc2 = c2;

var s1 = new Struct1();
var s2 = new Struct2();
object os1 = s1;
object os2 = s2;

bool b = false;

Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < 10000000; i++)
{
    b = c1.GetType() == typeof(Class1); // ~60ms
    b = c1 is Class1; // ~60ms

    b = c2.GetType() == typeof(Class1); // ~60ms
    b = c2 is Class1; // ~55ms

    b = oc1.GetType() == typeof(Class1); // ~60ms
    b = oc1 is Class1; // ~68ms

    b = oc2.GetType() == typeof(Class1); // ~60ms
    b = oc2 is Class1; // ~68ms


    b = s1.GetType() == typeof(Struct1); // ~150ms
    b = s1 is Struct1; // ~50ms

    b = s2.GetType() == typeof(Struct1); // ~150ms
    b = s2 is Struct1; // ~50ms

    b = os1.GetType() == typeof(Struct1); // ~60ms
    b = os1 is Struct1; // ~64ms

    b = os2.GetType() == typeof(Struct1); // ~60ms
    b = os2 is Struct1; // ~64ms


    b = GetType1<Class1, Class1>(c1); // ~178ms
    b = GetType2<Class1, Class1>(c1); // ~98ms
    b = Is<Class1, Class1>(c1); // ~78ms

    b = GetType1<Class1, Class2>(c2); // ~178ms
    b = GetType2<Class1, Class2>(c2); // ~96ms
    b = Is<Class1, Class2>(c2); // ~69ms

    b = GetType1<Class1, object>(oc1); // ~178ms
    b = Is<Class1, object>(oc1); // ~69ms

    b = GetType1<Class1, object>(oc2); // ~178ms
    b = Is<Class1, object>(oc2); // ~69ms


    b = GetType1<Struct1, Struct1>(s1); // ~272ms
    b = GetType2<Struct1, Struct1>(s1); // ~140ms
    b = Is<Struct1, Struct1>(s1); // ~163ms

    b = GetType1<Struct1, Struct2>(s2); // ~272ms
    b = GetType2<Struct1, Struct2>(s2); // ~140ms
    b = Is<Struct1, Struct2>(s2); // ~163ms

    b = GetType1<Struct1, object>(os1); // ~178ms
    b = Is<Struct1, object>(os1); // ~64ms

    b = GetType1<Struct1, object>(os2); // ~178ms
    b = Is<Struct1, object>(os2); // ~64ms
}

sw.Stop();
MessageBox.Show(sw.Elapsed.TotalMilliseconds.ToString());

Dan jenisnya:

sealed class Class1 { }
sealed class Class2 { }
struct Struct1 { }
struct Struct2 { }

Kesimpulan:

  1. Memanggil GetTypedi structs lebih lambat. GetTypedidefinisikan pada objectkelas yang tidak dapat diganti dalam sub tipe dan dengan demikian structperlu dikotak untuk dipanggil GetType.

  2. Pada instance objek, GetTypelebih cepat, tetapi sangat marginal.

  3. Pada jenis generik, jika Tyaitu class, maka isjauh lebih cepat. Jika Tini struct, maka isjauh lebih cepat daripada GetTypetetapi typeof(T)jauh lebih cepat dari keduanya. Dalam kasus Tsedang class, typeof(T)tidak dapat diandalkan karena berbeda dari tipe yang mendasarinya sebenarnya t.GetType.

Singkatnya, jika Anda memiliki sebuah objectinstance, gunakan GetType. Jika Anda memiliki classtipe generik , gunakan is. Jika Anda memiliki structtipe generik , gunakan typeof(T). Jika Anda tidak yakin apakah tipe generik adalah tipe referensi atau tipe nilai, gunakan is. Jika Anda ingin konsisten dengan satu gaya selalu (untuk tipe tertutup), gunakan is..

nawfal
sumber
1
Pada kenyataannya, tidak peduli sama sekali. Gunakan apa yang paling masuk akal.
nawfal