jest vs typeof


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)


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.


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 .


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...


1
W rzeczywistości nie obchodzi mnie to wcale. Użyj tego, co ma największy sens.
nawfal
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.