è vs typeof


Risposte:


167

Questo dovrebbe rispondere a quella domanda, e poi alcuni.

La seconda linea, if (obj.GetType() == typeof(ClassA)) {} è più veloce, per coloro che non vogliono leggere l'articolo.

(Ricorda che non fanno la stessa cosa)


1
+1: In passato mi chiedevo perché il compilatore C # non si compilasse typeof(string).TypeHandlecon l' ldtokenistruzione CIL, ma sembra che CLR se ne occupi in JIT. Richiede ancora alcuni codici operativi aggiuntivi ma è un'applicazione più generalizzata dell'ottimizzazione.
Sam Harwell,

2
Leggi higherlogics.blogspot.ca/2013/09/… - eseguono nuovamente il test per diversi framework e x86 vs x64 con risultati molto diversi.
Bloke CAD,

1
Si noti che questo vale solo per i tipi di riferimento. E la differenza di velocità non è così significativa. Data la penalità di boxe in caso di tipi di valore per GetType, isè sempre una scelta più sicura per quanto riguarda le prestazioni. Ovviamente fanno cose diverse.
nawfal,

Se lo metti in Resharper suggerisce di cambiarlo in "is"!
Rob Sedgwick,

@nawfal, inizialmente pensavo che il tuo punto sulla penalità di boxe avesse senso per i tipi di struttura, ma dato che stiamo testando una object obj;variabile, non è già inscatolata quando tende a essere testata? C'è un caso in cui è necessario testare il tipo di qualcosa e non è già inscatolato come oggetto?
Rob Parker,

193

Importa che è più veloce, se non fanno la stessa cosa? Confrontare le prestazioni delle dichiarazioni con significati diversi sembra una cattiva idea.

isti dice se l'oggetto si implementa ClassAovunque nel suo tipo di gerarchia. GetType()ti parla del tipo più derivato.

Non è la stessa cosa


7
È importante, perché nel mio caso sono sicuro che restituiscono lo stesso risultato.
ilitirit,

37
@ [ilitirit]: restituiscono lo stesso risultato in questo momento, ma se aggiungi una sottoclasse in un secondo momento non lo faranno
Steven A. Lowe,

13
L'ottimizzazione ora renderà il tuo codice fragile e difficile da mantenere.
ICR

9
Le mie lezioni sono sigillate.
ilitirit,

26

Non fanno la stessa cosa. Il primo funziona se obj è di tipo ClassA o di qualche sottoclasse di ClassA. Il secondo corrisponderà solo agli oggetti di tipo ClassA. Il secondo sarà più veloce poiché non deve controllare la gerarchia di classi.

Per coloro che vogliono conoscere il motivo, ma non vogliono leggere l'articolo a cui si fa riferimento è vs typeof .


1
@amitjha Sono un po 'preoccupato che, poiché quel test è stato eseguito in Mono, non include le ottimizzazioni JIT a cui fa riferimento l'articolo. Dal momento che l'articolo mostra il contrario, nella mia mente la domanda è aperta. In ogni caso, confrontare l'esecuzione di operazioni che fanno cose diverse a seconda del tipo sembra un esercizio senza valore. Usa l'operazione che corrisponde al comportamento che ti serve, non quello che è "più veloce"
tvanfosson

16

Ho fatto alcuni benchmarking in cui fanno lo stesso - tipi sigillati.

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

Le funzioni generiche per verificare i tipi generici:

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

Ho provato anche per tipi personalizzati e i risultati sono stati coerenti:

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

E i tipi:

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

Inferenza:

  1. Chiamando GetTypeil structs è più lento. GetTypeè definito su una objectclasse che non può essere sovrascritta nei sottotipi e quindi structdeve essere inscatolata per essere chiamata GetType.

  2. Su un'istanza di oggetto, GetTypeè più veloce, ma molto marginale.

  3. Sul tipo generico, se lo Tè class, allora isè molto più veloce. Se lo Tè struct, allora isè molto più veloce di GetTypema typeof(T)è molto più veloce di entrambi. In casi di Tessere class, typeof(T)non è affidabile poiché è diverso dal tipo di base reale t.GetType.

In breve, se si dispone di objectun'istanza, utilizzare GetType. Se hai un classtipo generico , usa is. Se hai un structtipo generico , usa typeof(T). Se non si è sicuri che il tipo generico sia di tipo di riferimento o di valore, utilizzare is. Se vuoi essere sempre coerente con uno stile (per i tipi sigillati), usa is..


1
In realtà, non importa affatto. Usa ciò che ha più senso.
nawfal,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.