2008-10-08 8 views
118

Lequel de ces morceaux de code est plus rapide?est vs typeof

if (obj is ClassA) {} 

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

Edit: Je suis conscient qu'ils ne font pas la même chose.

+1

Répondu à une question similaire ici: http://stackoverflow.com/questions/57701/what-are-the-performance-characteristics -of-is-reflection-in-C# 57713 – swilliams

Répondre

142

This should answer that question, and then some.

La deuxième ligne, if (obj.GetType() == typeof(ClassA)) {}, est plus rapide, pour ceux qui ne veulent pas lire l'article.

+1

+1: Dans le passé, je me demandais pourquoi le compilateur C# ne compilait pas 'typeof (string) .TypeHandle' dans l'instruction CIL' ldtoken', mais il ressemble à la CLR prend soin de lui dans le JIT. Il faut encore quelques opcodes supplémentaires mais c'est une application plus généralisée de l'optimisation. –

+2

Lire aussi http://higherlogics.blogspot.ca/2013/09/clr-cost-of-dynamic-type-tests.html aussi - ils retestent pour différents frameworks et x86 vs x64 avec des résultats très différents. –

+1

Veuillez noter que ceci est vrai seulement pour les types de référence. Et la différence de vitesse n'est pas si importante. Compte tenu de la pénalité de boxe en cas de types de valeur pour 'GetType',' is' est toujours un choix plus sûr en ce qui concerne les performances. Bien sûr, ils font des choses différentes. – nawfal

23

Ils ne font pas la même chose. Le premier fonctionne si obj est de type ClassA ou d'une sous-classe de ClassA. La seconde ne correspond qu'aux objets de type ClassA. Le second sera plus rapide puisqu'il n'aura pas à vérifier la hiérarchie des classes. Pour ceux qui veulent connaître la raison, mais ne veulent pas lire l'article référencé dans is vs typeof.

+0

http://stackoverflow.com/q/27813304 dit 'Object is Type' sera plus rapide. –

+1

@amitjha Je suis un peu inquiet parce que ce test a été exécuté sous Mono qu'il n'inclut pas les optimisations JIT référencées dans l'article. Puisque l'article montre le contraire, dans mon esprit la question est ouverte. En tout état de cause, comparer la performance d'opérations qui font des choses différentes selon le type semble un exercice sans valeur. Utilisez l'opération qui correspond au comportement dont vous avez besoin, pas celui qui est "plus rapide". – tvanfosson

153

Est-ce important de savoir ce qui est le plus rapide, s'ils ne font pas la même chose? Comparer la performance des énoncés avec des significations différentes semble être une mauvaise idée.

is indique si l'objet implémente ClassA n'importe où dans sa hiérarchie de type. GetType() vous indique le type le plus dérivé.

Pas la même chose.

+6

Cela importe, car dans mon cas, je suis certain qu'ils retournent le même résultat. – ilitirit

+28

@ [ilitirit]: ils retournent le même résultat en ce moment, mais si vous ajoutez une sous-classe plus tard, ils ne le feront pas –

+10

L'optimisation rendra votre code fragile et difficile à maintenir. – ICR

9

J'ai fait quelques analyses comparatives où ils font la même chose - les types scellés.

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

Les fonctions génériques de tester pour les types génériques:

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

J'ai essayé pour les types personnalisés ainsi, et les résultats étaient cohérents:

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

Et les types:

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

Inférence:

  1. Appel GetType sur struct s est plus lent.GetType est défini sur object classe qui ne peut pas être remplacée dans les sous-types et donc struct s doivent être encadrés pour être appelé GetType.

  2. Sur une instance d'objet, GetType est plus rapide, mais très marginal.

  3. Sur type générique, si T est class, alors is est beaucoup plus rapide. Si T est struct, alors is est beaucoup plus rapide que GetType mais typeof(T) est beaucoup plus rapide que les deux. Dans les cas de T étant class, typeof(T) n'est pas fiable car son différent du type sous-jacent réel t.GetType.

En bref, si vous avez une instance object, utilisez GetType. Si vous avez un type générique class, utilisez is. Si vous avez un type générique struct, utilisez typeof(T). Si vous ne savez pas si le type générique est le type de référence ou le type de valeur, utilisez is. Si vous voulez toujours être cohérent avec un style (pour les types scellés), utilisez is ..

+0

En réalité, ne vous souciez pas du tout. Utilisez ce qui a le plus de sens. – nawfal