156

Which of these pieces of code is faster?

if (obj is ClassA) {}

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

Edit: I'm aware that they don't do the same thing.

vvvvv
  • 25,404
  • 19
  • 49
  • 81
ilitirit
  • 16,016
  • 18
  • 72
  • 111
  • 1
    Answered a similar question here: http://stackoverflow.com/questions/57701/what-are-the-performance-characteristics-of-is-reflection-in-c#57713 – swilliams Oct 08 '08 at 20:21

4 Answers4

201

Does it matter which is faster, if they don't do the same thing? Comparing the performance of statements with different meaning seems like a bad idea.

is tells you if the object implements ClassA anywhere in its type heirarchy. GetType() tells you about the most-derived type.

Not the same thing.

Jay Bazuzi
  • 45,157
  • 15
  • 111
  • 168
173

This should answer that question, and then some.

The second line, if (obj.GetType() == typeof(ClassA)) {}, is faster, for those that don't want to read the article.

(Be aware that they don't do the same thing)

MagicKat
  • 9,695
  • 6
  • 32
  • 43
  • 1
    +1: In the past I wondered why the C# compiler didn't compile `typeof(string).TypeHandle` to the `ldtoken` CIL instruction, but it looks like the CLR takes care of it in the JIT. It still takes a few extra opcodes but it's a more generalized application of the optimization. – Sam Harwell Feb 22 '10 at 18:39
  • 2
    Read http://higherlogics.blogspot.ca/2013/09/clr-cost-of-dynamic-type-tests.html too - they retest for different frameworks and x86 vs x64 with widely differing results. – CAD bloke Jul 29 '14 at 11:21
  • 1
    Please note this is true only for reference types. And the speed difference is not that significant. Given the boxing penalty in case of value types for `GetType`, `is` is always a safer choice as far as performance is concerned. Of course they do different things. – nawfal Aug 04 '14 at 07:31
  • If you put that in Resharper suggests changing it to "is"! – Rob Sedgwick Jan 13 '15 at 17:20
  • @nawfal, I initially thought your point about the boxing penalty made sense for struct types, but given that we're testing an `object obj;` variable, isn't it already boxed when this tends to be tested? Is there a case where you need to test the type of something and it isn't already boxed as an object? – Rob Parker Nov 02 '16 at 19:50
  • @RobSedgwick, I wonder if the situation has changed since .NET 2.0 when the specially-optimized `GetType() == typeof` approach was the fastest (least wasteful) and FxCop warned not to use `is` (`as` is better, but not possible for struct types; FxCop may not have warned for `is` with a struct type). Perhaps `is` is more optimized in .NET 4.x than it used to be in .NET 2.0? – Rob Parker Nov 02 '16 at 19:54
  • @RobParker if the value type is already boxed as an `object` variable, then you are right, calling `GetType` makes no difference. But I am not sure if that is the case in the given link. – nawfal Nov 03 '16 at 18:37
  • @nawfal, what other declared type could the variable in question be that could possibly hold a struct type? Can a struct inherit, or implement an interface (I'm thinking "no", but maybe the latter is allowed)? – Rob Parker Nov 22 '16 at 18:51
  • @RobParker Structs can implement interfaces but if you declare them as their interface type, then yes they will be boxed. For e.g. `IInterfaceForMyStruct myStruct = new MyStruct();` leads to a boxed variable. Of course all structs and enums inherit from a common base class `ValueType` (which in turn inherit from `object` leading to unified type system) but user defined value types cant *inherit* anything, only *implement*. – nawfal Nov 22 '16 at 19:00
  • @nawfal, okay, good clarification. But how, then, could you have this problem and not already have a boxed variable? If it was a declared specific value type there'd be no need to test its type (especially with no inheritance possible). Can you declare a ValueType variable which can hold any value type, and would that not have to be boxed already? I would think that supporting the polymorphism would require it to be boxed in that case, too. – Rob Parker Nov 28 '16 at 18:59
  • @RobParker you're right. If you declare your value type as ValueType the variable has to be boxed. If variable is declared as a specific value type you can always rely on `mystruct is MyStruct` rather than `mystruct.GetType()` which will cause boxing. Perhaps useful in generic contexts. – nawfal Nov 28 '16 at 19:07
  • The link leads to some menu. – Necronomicron Feb 25 '20 at 09:00
  • Dead link. Would someone have at least the reference of the article? – paradise Mar 27 '20 at 15:34
27

They don't do the same thing. The first one works if obj is of type ClassA or of some subclass of ClassA. The second one will only match objects of type ClassA. The second one will be faster since it doesn't have to check the class hierarchy.

For those who want to know the reason, but don't want to read the article referenced in is vs typeof.

Community
  • 1
  • 1
tvanfosson
  • 524,688
  • 99
  • 697
  • 795
  • 2
    @amitjha I'm a little concerned that because that test was run under Mono that it doesn't include the JIT optimizations referenced in the article. Since the article shows the opposite, in my mind the question is an open one. In any event, comparing performance of operations that do different things depending on the type seems a worthless exercise. Use the operation that matches the behavior you need, not the one that is "faster" – tvanfosson Apr 04 '16 at 12:58
19

I did some benchmarking where they do the same - sealed types.

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

The generic functions to test for generic types:

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

I tried for custom types as well and the results were consistent:

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

And the types:

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

Inference:

  1. Calling GetType on structs is slower. GetType is defined on object class which can't be overridden in sub types and thus structs need to be boxed to be called GetType.

  2. On an object instance, GetType is faster, but very marginally.

  3. On generic type, if T is class, then is is much faster. If T is struct, then is is much faster than GetType but typeof(T) is much faster than both. In cases of T being class, typeof(T) is not reliable since its different from actual underlying type t.GetType.

In short, if you have an object instance, use GetType. If you have a generic class type, use is. If you have a generic struct type, use typeof(T). If you are unsure if generic type is reference type or value type, use is. If you want to be consistent with one style always (for sealed types), use is..

nawfal
  • 70,104
  • 56
  • 326
  • 368