2008-10-08 16 views
118

Który z tych fragmentów kodu jest szybszy?jest vs typeof

if (obj is ClassA) {} 

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

Edit: Jestem świadomy, że nie robią to samo.

+1

. Odpowiedzi na podobne pytanie tutaj: http://stackoverflow.com/questions/57701/what-are-to-performance-characteristics -of-is-reflection-in-C# 57713 – swilliams

Odpowiedz

142

This should answer that question, and then some.

Druga linia, if (obj.GetType() == typeof(ClassA)) {}, jest szybszy, dla tych, którzy nie chcą, aby przeczytać artykuł.

+1

+1: W przeszłości zastanawiałem się, dlaczego kompilator C# nie kompilował 'typeof (string) .TypeHandle' do instrukcji' ldtoken' CIL, ale wygląda jak CLR zajmuje się tym w JIT. Nadal zajmuje kilka dodatkowych kodów, ale jest to bardziej ogólne zastosowanie optymalizacji. –

+2

Przeczytaj też http://higherlogics.blogspot.ca/2013/09/clr-cost-of-dynamic-type-tests.html - ponownie testują różne frameworki i x86 w porównaniu do x64 z bardzo różnymi wynikami. –

+1

Pamiętaj, że dotyczy to tylko typów referencji. Różnica prędkości nie jest tak znacząca. Biorąc pod uwagę karę boksowania w przypadku typów wartości dla 'GetType',' is' jest zawsze bezpieczniejszym wyborem pod względem wydajności. Oczywiście, że robią różne rzeczy. – nawfal

23

Nie robią tego samego. Pierwszy działa, gdy obiekt jest typu ClassA lub jakiejś podklasy klasy ClassA. Drugi dopasuje tylko obiekty typu ClassA. Drugi będzie szybszy, ponieważ nie musi sprawdzać hierarchii klas.

Dla tych, którzy chcą poznać przyczynę, ale nie chcą czytać artykułu wymienionego pod numerem is vs typeof.

+0

http://stackoverflow.com/q/27813304 mówi, że "Obiekt jest typu" będzie szybszy. –

+1

@amitjha Jestem trochę zaniepokojony tym, że ponieważ ten test był uruchamiany pod Mono, to nie zawiera optymalizacji JIT, o których mowa w artykule. Ponieważ artykuł pokazuje coś przeciwnego, moim zdaniem pytanie jest otwarte. W każdym razie porównywanie wydajności operacji, które robią różne rzeczy w zależności od typu, wydaje się bezwartościowym ćwiczeniem. Użyj operacji, która pasuje do zachowań, których potrzebujesz, a nie tej, która jest "szybsza". – tvanfosson

153

Czy to ma znaczenie, które jest szybsze, jeśli nie robią tego samego? Porównywanie wydajności wypowiedzi o różnym znaczeniu wydaje się złym pomysłem.

is mówi, czy obiekt implementuje ClassA w dowolnym miejscu w hierarchii typów. GetType() mówi o najbardziej pochodnym typie.

To nie to samo.

+6

To ma znaczenie, ponieważ w moim przypadku jestem pewien, że zwracają ten sam wynik. – ilitirit

+28

@ [ilitirit]: zwracają teraz ten sam wynik, ale jeśli dodasz podklasę później, to nie będzie to –

+10

Optymalizacja teraz sprawi, że twój kod będzie kruchy i trudny w utrzymaniu. – ICR

9

Zrobiłem benchmarking tam, gdzie robią to samo - typy zapieczętowane.

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

Funkcje ogólne przetestować dla typów generycznych:

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

Próbowałem dla typów niestandardowych, jak również i wyniki były zgodne:

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

oraz rodzaje:

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

Wnioskowanie:

  1. Wywołanie GetType na struct S jest wolniejszy.GetType jest zdefiniowana w klasie object, której nie można zastąpić w podtypach, a zatem struct s muszą być zapakowane, aby można było je nazwać GetType.

  2. W instancji obiektu numer GetType jest szybszy, ale bardzo marginalnie.

  3. typu generycznego, jeśli T jest class, następnie is jest znacznie szybsze. Jeśli T jest struct, wówczas is jest znacznie szybszy niż GetType, ale typeof(T) jest znacznie szybszy niż oba. W przypadku T bycie class, typeof(T) nie jest wiarygodne, ponieważ różni się od rzeczywistego bazowego typu t.GetType.

W skrócie, jeśli masz instancję object użyć GetType. Jeśli masz ogólny typ class, użyj is. Jeśli masz typowy typ struct, użyj typeof(T). Jeśli nie masz pewności, czy typ ogólny to typ odniesienia, czy typ wartości, użyj is. Jeśli chcesz zachować spójność z jednym stylem zawsze (w przypadku typów zapieczętowanych), użyj is ..

+0

W rzeczywistości nie obchodzi w ogóle. Użyj tego, co ma największy sens. – nawfal

Powiązane problemy