jest vs typeof

150

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

if (obj is ClassA) {}

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

Edycja: zdaję sobie sprawę, że nie robią tego samego.

ilitirit
źródło
1
Odpowiedziałem na podobne pytanie tutaj: stackoverflow.com/questions/57701/ ...
swilliams

Odpowiedzi:

167

To powinno odpowiedzieć na to pytanie, a potem na niektóre.

Drugi wiersz if (obj.GetType() == typeof(ClassA)) {}jest szybszy dla tych, którzy nie chcą czytać artykułu.

(Pamiętaj, że nie robią tego samego)

MagicKat
źródło
1
+1: W przeszłości zastanawiałem się, dlaczego kompilator C # nie skompilował się typeof(string).TypeHandledo ldtokeninstrukcji CIL, ale wygląda na to, że CLR zajmuje się tym w JIT. Nadal wymaga kilku dodatkowych instrukcji, ale jest to bardziej uogólniona aplikacja optymalizacji.
Sam Harwell
2
Przeczytaj także wyższą logikę.blogspot.ca/2013/09/ ... - ponownie testują dla różnych platform i x86 vs x64 z bardzo różnymi wynikami.
Facet z CAD
1
Należy pamiętać, że dotyczy to tylko typów referencyjnych. A różnica prędkości nie jest tak znacząca. Biorąc pod uwagę karę bokserską w przypadku typów wartości GetType, isjest to zawsze bezpieczniejszy wybór, jeśli chodzi o wydajność. Oczywiście robią różne rzeczy.
nawfal
Jeśli umieścisz to w Resharper sugeruje zmianę na „jest”!
Rob Sedgwick
@nawfal, początkowo myślałem, że twój punkt widzenia dotyczący kary bokserskiej ma sens w przypadku typów struktur, ale biorąc pod uwagę, że testujemy object obj;zmienną, czy nie jest już opakowana, gdy ma być testowana? Czy jest przypadek, w którym musisz przetestować typ czegoś i nie jest to już opakowane jako obiekt?
Rob Parker
193

Czy ma znaczenie, który jest szybszy, jeśli nie robią tego samego? Porównanie wykonania wypowiedzi o różnym znaczeniu wydaje się złym pomysłem.

isinformuje, czy obiekt implementuje ClassAgdziekolwiek w swojej hierarchii typów. GetType()informuje o typie najbardziej pochodnym.

To nie to samo.

Jay Bazuzi
źródło
7
Ma to znaczenie, ponieważ w moim przypadku jestem pewien, że zwracają ten sam wynik.
ilitirit
37
@ [ilitirit]: teraz zwracają ten sam wynik, ale jeśli później dodasz podklasę, nie
Steven A. Lowe
13
Optymalizacja teraz sprawi, że Twój kod będzie kruchy i trudny w utrzymaniu.
ICR
9
Moje zajęcia są zapieczętowane.
ilitirit
26

Nie robią tego samego. Pierwsza działa, jeśli obj jest typu ClassA lub jakiejś podklasy ClassA. Drugi będzie pasował tylko do obiektów 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, do którego się odwołuje jest vs typeof .

tvanfosson
źródło
1
@amitjha Jestem trochę zaniepokojony tym, że ponieważ ten test został uruchomiony w Mono, nie zawiera on optymalizacji JIT, o których mowa w artykule. Ponieważ artykuł pokazuje coś przeciwnego, moim zdaniem pytanie jest otwarte. W każdym razie porównywanie wykonywania operacji, które robią różne rzeczy w zależności od rodzaju, wydaje się bezwartościowym ćwiczeniem. Użyj operacji, która odpowiada wymaganemu zachowaniu, a nie tej, która jest „szybsza”
tvanfosson
16

Zrobiłem kilka testów porównawczych, gdzie robią to samo - zapieczętowane typy.

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 do testowania dla typów ogólnych:

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 również dla niestandardowych typów i wyniki były spójne:

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 typy:

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

Wnioskowanie:

  1. Dzwoniąc GetTypena structs wolniej. GetTypejest zdefiniowana w objectklasie, której nie można przesłonić w typach podrzędnych, a zatem structaby wywołać, należy ją spakować GetType.

  2. W instancji obiektu GetTypejest szybszy, ale bardzo marginalnie.

  3. Na typie ogólnym, jeśli Ttak class, isjest znacznie szybsze. Jeśli Ttak struct, to isjest znacznie szybsze niż, GetTypeale typeof(T)jest znacznie szybsze niż oba. W przypadku Tistoty class, typeof(T)nie jest wiarygodne, ponieważ jego różni się od rzeczywistego typu bazowego t.GetType.

Krótko mówiąc, jeśli masz objectinstancję, użyj GetType. Jeśli masz classtyp ogólny , użyj is. Jeśli masz structtyp ogólny , użyj typeof(T). Jeśli nie masz pewności, czy typ ogólny jest typem referencyjnym, czy typem wartości, użyj is. Jeśli chcesz zawsze zachować spójność z jednym stylem (dla typów zapieczętowanych), użyj is...

nawfal
źródło
1
W rzeczywistości nie obchodzi mnie to wcale. Użyj tego, co ma największy sens.
nawfal