es vs typeof

150

¿Cuál de estos fragmentos de código es más rápido?

if (obj is ClassA) {}

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

Editar: Soy consciente de que no hacen lo mismo.

ilitirit
fuente
1
Respondí una pregunta similar aquí: stackoverflow.com/questions/57701/…
swilliams

Respuestas:

167

Esto debería responder esa pregunta, y algo más.

La segunda línea, if (obj.GetType() == typeof(ClassA)) {}es más rápida, para aquellos que no quieren leer el artículo.

(Tenga en cuenta que no hacen lo mismo)

MagicKat
fuente
1
+1: En el pasado me preguntaba por qué el compilador de C # no compilaba typeof(string).TypeHandlela ldtokeninstrucción CIL, pero parece que el CLR se encarga de eso en el JIT. Todavía requiere algunos códigos de operación adicionales, pero es una aplicación más generalizada de la optimización.
Sam Harwell
2
Lea higherlogics.blogspot.ca/2013/09/… también: vuelven a probar diferentes marcos y x86 vs x64 con resultados muy diferentes.
CAD bloke
1
Tenga en cuenta que esto es cierto solo para los tipos de referencia. Y la diferencia de velocidad no es tan significativa. Dada la penalización de boxeo en caso de tipos de valor GetType, issiempre es una opción más segura en lo que respecta al rendimiento. Por supuesto que hacen cosas diferentes.
nawfal
¡Si pones eso en Resharper sugiere cambiarlo a "es"!
Rob Sedgwick el
@nawfal, inicialmente pensé que tu punto sobre la penalización del boxeo tenía sentido para los tipos de estructura, pero dado que estamos probando una object obj;variable, ¿no está ya encuadrada cuando esto tiende a probarse? ¿Hay algún caso en el que necesites probar el tipo de algo y aún no esté encuadrado como un objeto?
Rob Parker el
193

¿Importa cuál es más rápido si no hacen lo mismo? Comparar el desempeño de las declaraciones con diferentes significados parece una mala idea.

isle indica si el objeto se implementa ClassAen algún lugar de su jerarquía de tipos. GetType()le informa sobre el tipo más derivado.

No es lo mismo.

Jay Bazuzi
fuente
77
Sí importa, porque en mi caso estoy seguro de que devuelven el mismo resultado.
ilitirit
37
@ [ilitirit]: ahora devuelven el mismo resultado, pero si agrega una subclase más tarde, no lo harán
Steven A. Lowe
13
La optimización ahora hará que su código sea frágil y difícil de mantener.
ICR
9
Mis clases están selladas.
ilitirit
26

No hacen lo mismo. El primero funciona si obj es de tipo ClassA o de alguna subclase de ClassA. El segundo solo coincidirá con objetos de tipo ClassA. El segundo será más rápido ya que no tiene que verificar la jerarquía de clases.

Para aquellos que quieren saber la razón, pero no quieren leer el artículo al que se hace referencia en es vs typeof .

tvanfosson
fuente
1
@amitjha Estoy un poco preocupado porque, debido a que esa prueba se ejecutó en Mono, no incluye las optimizaciones JIT mencionadas en el artículo. Como el artículo muestra lo contrario, en mi opinión, la pregunta es abierta. En cualquier caso, comparar el rendimiento de las operaciones que hacen cosas diferentes según el tipo parece un ejercicio inútil. Utilice la operación que coincida con el comportamiento que necesita, no el que es "más rápido"
tvanfosson
16

Hice algunas evaluaciones comparativas donde hacen lo mismo: tipos sellados.

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

Las funciones genéricas para probar los tipos genéricos:

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

También probé los tipos personalizados y los resultados fueron consistentes:

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

Y los tipos:

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

Inferencia:

  1. Llamar GetTypea structs es más lento. GetTypese define en una objectclase que no se puede anular en subtipos y, por lo tanto, se structdebe encuadrar para poder llamar GetType.

  2. En una instancia de objeto, GetTypees más rápido, pero muy marginal.

  3. En el tipo genérico, si Tes class, entonces ises mucho más rápido. Si Tes así struct, entonces ises mucho más rápido que, GetTypepero typeof(T)es mucho más rápido que ambos. En casos de Tser class, typeof(T)no es confiable ya que es diferente del tipo subyacente real t.GetType.

En resumen, si tiene una objectinstancia, use GetType. Si tiene un classtipo genérico , use is. Si tiene un structtipo genérico , use typeof(T). Si no está seguro si el tipo genérico es tipo de referencia o tipo de valor, use is. Si quiere ser consistente con un estilo siempre (para tipos sellados), use is...

nawfal
fuente
1
En realidad, no me importa en absoluto. Usa lo que tenga más sentido.
nawfal