ist vs typeof

150

Welcher dieser Codeteile ist schneller?

if (obj is ClassA) {}

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

Edit: Mir ist bewusst, dass sie nicht das Gleiche tun.

ilitirit
quelle
1
Beantwortete eine ähnliche Frage hier: stackoverflow.com/questions/57701/…
swilliams

Antworten:

167

Dies sollte diese und noch einige weitere Fragen beantworten.

Die zweite Zeile if (obj.GetType() == typeof(ClassA)) {}ist schneller für diejenigen, die den Artikel nicht lesen möchten.

(Seien Sie sich bewusst, dass sie nicht dasselbe tun)

MagicKat
quelle
1
+1: In der Vergangenheit habe ich mich gefragt, warum der C # -Compiler nicht typeof(string).TypeHandlemit der ldtokenCIL-Anweisung kompiliert wurde , aber es sieht so aus, als würde sich die CLR in der JIT darum kümmern. Es werden noch einige zusätzliche Opcodes benötigt, aber es handelt sich um eine allgemeinere Anwendung der Optimierung.
Sam Harwell
2
Lesen Sie auch highlogics.blogspot.ca/2013/09/… - sie testen erneut für verschiedene Frameworks und x86 vs x64 mit sehr unterschiedlichen Ergebnissen.
CAD Kerl
1
Bitte beachten Sie, dass dies nur für Referenztypen gilt. Und der Geschwindigkeitsunterschied ist nicht so signifikant. Angesichts der Box - Strafe bei Werttypen für GetType, isist immer eine sicherere Wahl so weit wie Leistung angeht. Natürlich machen sie verschiedene Dinge.
Nawfal
Wenn Sie dies in Resharper eingeben, wird vorgeschlagen, es in "ist" zu ändern!
Rob Sedgwick
@nawfal, ich dachte anfangs, dass Ihr Standpunkt zur Boxstrafe für Strukturtypen sinnvoll ist, aber angesichts der Tatsache, dass wir eine object obj;Variable testen , ist sie nicht bereits boxen, wenn dies tendenziell getestet wird? Gibt es einen Fall, in dem Sie den Typ von etwas testen müssen und es nicht bereits als Objekt verpackt ist?
Rob Parker
193

Ist es wichtig, was schneller ist, wenn sie nicht dasselbe tun? Der Vergleich der Leistung von Aussagen mit unterschiedlicher Bedeutung scheint eine schlechte Idee zu sein.

isteilt Ihnen mit, ob das Objekt ClassAirgendwo in seiner Typ-Hierarchie implementiert ist. GetType()informiert Sie über den am meisten abgeleiteten Typ.

Nicht dasselbe.

Jay Bazuzi
quelle
7
Es ist wichtig, denn in meinem Fall bin ich mir sicher, dass sie das gleiche Ergebnis liefern.
ilitirit
37
@ [ilitirit]: Sie geben das gleiche Ergebnis im Moment zurück, aber wenn Sie später eine Unterklasse hinzufügen, werden sie nicht
Steven A. Lowe
13
Wenn Sie jetzt optimieren, wird Ihr Code spröde und schwer zu pflegen.
ICR
9
Meine Klassen sind versiegelt.
ilitirit
26

Sie machen nicht das Gleiche. Das erste funktioniert, wenn obj vom Typ ClassA oder einer Unterklasse von ClassA ist. Der zweite entspricht nur Objekten vom Typ ClassA. Der zweite ist schneller, da die Klassenhierarchie nicht überprüft werden muss.

Für diejenigen, die den Grund wissen möchten, aber den Artikel, auf den verwiesen wird, nicht lesen möchten ist vs typeof .

Tvanfosson
quelle
1
@amitjha Ich bin ein wenig besorgt darüber, dass dieser Test, der unter Mono ausgeführt wurde, die im Artikel genannten JIT-Optimierungen nicht enthält. Da der Artikel das Gegenteil zeigt, ist die Frage meiner Meinung nach offen. In jedem Fall scheint es eine wertlose Übung zu sein, die Leistung von Operationen zu vergleichen, die je nach Typ unterschiedliche Aufgaben ausführen. Verwenden Sie die Operation, die dem von Ihnen benötigten Verhalten entspricht, nicht die, die "schneller" ist
tvanfosson
16

Ich habe ein Benchmarking durchgeführt, bei dem die gleichen versiegelten Typen verwendet werden.

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

Die generischen Funktionen zum Testen auf generische Typen:

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;
}

Ich habe auch nach benutzerdefinierten Typen gesucht und die Ergebnisse waren konsistent:

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

Und die Typen:

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

Inferenz:

  1. Das Aufrufen GetTypevon structs ist langsamer. GetTypewird für eine objectKlasse definiert , die in Untertypen nicht überschrieben werden kann und daher structzum Aufrufen eingerahmt werden muss GetType.

  2. Auf einer Objektinstanz GetTypeist schneller, aber sehr marginal.

  3. Wenn dies beim generischen Typ der Fall Tist class, isist dies viel schneller. Wenn Tja struct, dann isist es viel schneller als, GetTypeaber typeof(T)viel schneller als beide. In Fällen von TWesen class, typeof(T)ist seit seiner verschiedenen nicht zuverlässig von den tatsächlichen zugrunde liegenden Typ t.GetType.

Kurz gesagt, wenn Sie eine objectInstanz haben, verwenden Sie GetType. Wenn Sie einen generischen classTyp haben, verwenden Sie is. Wenn Sie einen generischen structTyp haben, verwenden Sie typeof(T). Wenn Sie sich nicht sicher sind, ob der generische Typ der Referenztyp oder der Werttyp ist, verwenden Sie is. Wenn Sie immer mit einem Stil übereinstimmen möchten (für versiegelte Typen), verwenden Sie is..

nawfal
quelle
1
In Wirklichkeit ist das überhaupt egal. Verwenden Sie, was am sinnvollsten ist.
Nawfal