est vs typeof

150

Lequel de ces morceaux de code est le plus rapide?

if (obj is ClassA) {}

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

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

ilitirit
la source
1
A répondu à une question similaire ici: stackoverflow.com/questions/57701/…
swilliams

Réponses:

167

Cela devrait répondre à cette question, et plus encore.

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

(Sachez qu'ils ne font pas la même chose)

MagicKat
la source
1
+1: Dans le passé, je me demandais pourquoi le compilateur C # ne se compilait pas typeof(string).TypeHandleavec l' ldtokeninstruction CIL, mais il semble que le CLR s'en occupe dans le JIT. Il faut encore quelques opcodes supplémentaires mais c'est une application plus généralisée de l'optimisation.
Sam Harwell
2
Lisez aussi Higherlogics.blogspot.ca/2013/09/… - ils retestent pour différents frameworks et x86 vs x64 avec des résultats très différents.
Bloke CAD
1
Veuillez noter que cela n'est vrai que pour les types de référence. Et la différence de vitesse n'est pas si significative. Compte tenu de la pénalité de boxe en cas de types de valeur pour GetType, isest toujours un choix plus sûr en ce qui concerne les performances. Bien sûr, ils font des choses différentes.
nawfal
Si vous mettez cela dans Resharper suggère de le changer en "est"!
Rob Sedgwick
@nawfal, j'ai d'abord pensé que votre remarque sur la pénalité de boxe avait du sens pour les types de struct, mais étant donné que nous testons une object obj;variable, n'est-elle pas déjà encadrée lorsque cela a tendance à être testé? Y a-t-il un cas où vous devez tester le type de quelque chose et qu'il n'est pas déjà encadré en tant qu'objet?
Rob Parker
193

Est-ce que ce qui est le plus rapide importe, s'ils ne font pas la même chose? Comparer les performances d'énoncés ayant une signification différente semble être une mauvaise idée.

isvous indique si l'objet implémente ClassAn'importe où dans son héritage de type. GetType()vous indique le type le plus dérivé.

Pas la même chose.

Jay Bazuzi
la source
7
C'est important, car dans mon cas, je suis sûr qu'ils renvoient le même résultat.
ilitirit
37
@ [ilitirit]: ils renvoient le même résultat pour le moment, mais si vous ajoutez une sous-classe plus tard, ils ne le feront pas
Steven A. Lowe
13
L'optimisation maintenant rendra votre code fragile et difficile à maintenir.
ICR
9
Mes cours sont scellés.
ilitirit
26

Ils ne font pas la même chose. Le premier fonctionne si obj est de type ClassA ou d'une sous-classe de ClassA. Le second ne correspondra qu'aux objets de type ClassA. Le second sera plus rapide car il n'a pas besoin de 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 est vs typeof .

Tvanfosson
la source
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. Dans tous les cas, comparer les performances d'opérations qui font des choses différentes selon le type semble un exercice inutile. Utilisez l'opération qui correspond au comportement dont vous avez besoin, pas celle qui est "plus rapide"
tvanfosson
16

J'ai fait des analyses comparatives là où ils font la même chose - des 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 à 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 également essayé des types personnalisés 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. L'appel GetTypesur structs est plus lent. GetTypeest défini sur une objectclasse qui ne peut pas être remplacée dans les sous-types et donc les structs doivent être encadrés pour être appelés GetType.

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

  3. Sur le type générique, si Tc'est le cas class, alors isc'est beaucoup plus rapide. Si Tc'est le cas struct, alors isc'est beaucoup plus rapide que GetTypemais typeof(T)c'est beaucoup plus rapide que les deux. En cas d' Têtre class, typeof(T)n'est pas fiable car il est différent du type sous-jacent réel t.GetType.

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

nawfal
la source
1
En réalité, ne vous inquiétez pas du tout. Utilisez ce qui a le plus de sens.
nawfal