Comprobación de tipo: typeof, GetType, o is?


He visto a muchas personas usar el siguiente código:

Type t = typeof(obj1);
if (t == typeof(int))
    // Some code here

Pero sé que también podrías hacer esto:

if (obj1.GetType() == typeof(int))
    // Some code here

O esto:

if (obj1 is int)
    // Some code here

Personalmente, siento que el último es el más limpio, pero ¿hay algo que me falta? Cual es el mejor para usar, o es preferencia personal?

 1225
c#
Author: jasonh, 2009-06-11

14 answers

Todos son diferentes.

  • typeof toma un nombre de tipo (que se especifica en tiempo de compilación).
  • GetType obtiene el tipo de tiempo de ejecución de una instancia.
  • is devuelve true si una instancia está en el árbol de herencia.

Ejemplo

class Animal { } 
class Dog : Animal { }

void PrintTypes(Animal a) { 
    Console.WriteLine(a.GetType() == typeof(Animal)); // false 
    Console.WriteLine(a is Animal);                   // true 
    Console.WriteLine(a.GetType() == typeof(Dog));    // true
    Console.WriteLine(a is Dog);                      // true 
}

Dog spot = new Dog(); 
PrintTypes(spot);

¿Qué pasa con typeof(T)? ¿También se resuelve en tiempo de compilación?

Sí. T es siempre lo que el tipo de la expresión. Recuerde, un método genérico es básicamente un montón de métodos con la tipo apropiado. Ejemplo:

string Foo<T>(T parameter) { return typeof(T).Name; }

Animal probably_a_dog = new Dog();
Dog    definitely_a_dog = new Dog();

Foo(probably_a_dog); // this calls Foo<Animal> and returns "Animal"
Foo<Animal>(probably_a_dog); // this is exactly the same as above
Foo<Dog>(probably_a_dog); // !!! This will not compile. The parameter expects a Dog, you cannot pass in an Animal.

Foo(definitely_a_dog); // this calls Foo<Dog> and returns "Dog"
Foo<Dog>(definitely_a_dog); // this is exactly the same as above.
Foo<Animal>(definitely_a_dog); // this calls Foo<Animal> and returns "Animal". 
Foo((Animal)definitely_a_dog); // this does the same as above, returns "Animal"
 1528
Author: Jimmy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-07 03:22:47

Use typeofcuando desee obtener el tipo en tiempo de compilación. Use GetType cuando desee obtener el tipo en tiempo de ejecución. Rara vez hay casos para usar is ya que hace un cast y, en la mayoría de los casos, termina fundiendo la variable de todos modos.

Hay una cuarta opción que no ha considerado (especialmente si va a enviar un objeto al tipo que también encuentre); es usar as.

Foo foo = obj as Foo;

if (foo != null)
    // your code here

Esto solo usa un fundido mientras que este enfoque:

if (obj is Foo)
    Foo foo = (Foo)obj;

Requiere dos.

 166
Author: Andrew Hare,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-22 10:16:03

1.

Type t = typeof(obj1);
if (t == typeof(int))

Esto es ilegal, porque typeof solo funciona en tipos, no en variables. Supongo que obj1 es una variable. Por lo tanto, de esta manera typeof es estático, y hace su trabajo en tiempo de compilación en lugar de tiempo de ejecución.

2.

if (obj1.GetType() == typeof(int))

Esto es cierto si obj1 es exactamente de tipo int. Si obj1 deriva de int, la condición if será falsa.

3.

if (obj1 is int)

Esto es cierto si obj1 es un int, o si deriva de una clase llamada int, o si implementa una interfaz llamada int.

 61
Author: Scott Langham,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-11 19:17:26
Type t = typeof(obj1);
if (t == typeof(int))
    // Some code here

Esto es un error. El operador typeof en C# solo puede tomar nombres de tipo, no objetos.

if (obj1.GetType() == typeof(int))
    // Some code here

Esto funcionará, pero tal vez no como esperarías. Para los tipos de valor, como se ha mostrado aquí, es aceptable, pero para los tipos de referencia, solo devolvería true si el tipo era el exactamente el mismo tipo, no algo más en la jerarquía de herencia. Por ejemplo:

class Animal{}
class Dog : Animal{}

static void Foo(){
    object o = new Dog();

    if(o.GetType() == typeof(Animal))
        Console.WriteLine("o is an animal");
    Console.WriteLine("o is something else");
}

Esto imprimiría "o is something else", porque el tipo de o es Dog, no Animal. Puedes haga que esto funcione, sin embargo, si utiliza el método IsAssignableFrom de la clase Type.

if(typeof(Animal).IsAssignableFrom(o.GetType())) // note use of tested type
    Console.WriteLine("o is an animal");

Esta técnica todavía deja un problema importante, sin embargo. Si su variable es null, la llamada a GetType() lanzará una excepción NullReferenceException. Así que para que funcione correctamente, usted haría:

if(o != null && typeof(Animal).IsAssignableFrom(o.GetType()))
    Console.WriteLine("o is an animal");

Con esto, tienes un comportamiento equivalente de la palabra clave is. Por lo tanto, si este es el comportamiento que desea, debe usar la palabra clave is, que es más legible y más eficiente.

if(o is Animal)
    Console.WriteLine("o is an animal");

En la mayoría de los casos, sin embargo, la palabra clave is todavía no es lo que realmente quieres, porque generalmente no es suficiente solo saber que un objeto es de un cierto tipo. Por lo general, desea realmente usar ese objeto como una instancia de ese tipo, que también requiere fundirlo. Y así usted puede encontrarse escribiendo código como este:

if(o is Animal)
    ((Animal)o).Speak();

Pero eso hace que el CLR compruebe el tipo del objeto hasta dos veces. Lo comprobará una vez para satisfacer al operador is, y si o es de hecho un Animal, lo hacemos comprobar de nuevo para validar el cast.

Es más eficiente hacer esto en su lugar:

Animal a = o as Animal;
if(a != null)
    a.Speak();

El operador as es un cast que no lanzará una excepción si falla, en su lugar devuelve null. De esta manera, el CLR comprueba el tipo del objeto solo una vez, y después de eso, solo tenemos que hacer una comprobación nula, que es más eficiente.

Pero cuidado: muchas personas caen en una trampa con as. Debido a que no arroja excepciones, algunas personas piensan que es una " caja fuerte" y lo usan exclusivamente, evitando los moldes regulares. Esto conduce a errores como este:

(o as Animal).Speak();

En este caso, el desarrollador está asumiendo claramente que o siempre será un Animal, y mientras su suposición sea correcta, todo funciona bien. Pero si están equivocados, entonces lo que terminan aquí es un NullReferenceException. Con un reparto regular, habrían obtenido un InvalidCastException en su lugar, lo que habría identificado más correctamente el problema.

A veces, este error puede ser difícil de encontrar:

class Foo{
    readonly Animal animal;

    public Foo(object o){
        animal = o as Animal;
    }

    public void Interact(){
        animal.Speak();
    }
}

Este es otro caso en el que el desarrollador espera claramente que o sea un Animal cada vez, pero esto no es obvio en el constructor, donde se usa el molde as. No es obvio hasta que llegues al método Interact, donde se espera que el campo animal sea asignado positivamente. En este caso, no solo termina con una excepción engañosa, sino que no se lanza hasta potencialmente mucho más tarde que cuando ocurrió el error real.

En resumen:

  • Si solo necesita saber si un objeto es de algún tipo, use is.

  • Si necesita tratar un objeto como una instancia de cierto tipo, pero no sabe con certeza que el objeto será de ese tipo, use as y verifique null.

  • Si necesita tratar un objeto como una instancia de cierto tipo, y se supone que el objeto es de ese tipo, use un molde regular.

 40
Author: P Daddy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-11 17:02:47

Tenía una propiedad Type - para comparar y no podía usar is (como my_type is _BaseTypetoLookFor), pero podría usar estos:

base_type.IsInstanceOfType(derived_object);
base_type.IsAssignableFrom(derived_type);
derived_type.IsSubClassOf(base_type);

Observe que IsInstanceOfType y IsAssignableFrom devuelven true al comparar los mismos tipos, donde IsSubclassOf devolverá false. Y IsSubclassOf no funciona en interfaces, donde los otros dos lo hacen. (Ver también esta pregunta y respuesta.)

public class Animal {}
public interface ITrainable {}
public class Dog : Animal, ITrainable{}

Animal dog = new Dog();

typeof(Animal).IsInstanceOfType(dog);     // true
typeof(Dog).IsInstanceOfType(dog);        // true
typeof(ITrainable).IsInstanceOfType(dog); // true

typeof(Animal).IsAssignableFrom(dog.GetType());      // true
typeof(Dog).IsAssignableFrom(dog.GetType());         // true
typeof(ITrainable).IsAssignableFrom(dog.GetType()); // true

dog.GetType().IsSubclassOf(typeof(Animal));            // true
dog.GetType().IsSubclassOf(typeof(Dog));               // false
dog.GetType().IsSubclassOf(typeof(ITrainable)); // false
 11
Author: Yahoo Serious,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 10:31:37

Si estás usando C# 7, entonces es hora de actualizar la gran respuesta de Andrew Hare. Pattern matching ha introducido un buen atajo que nos da una variable mecanografiada dentro del contexto de la instrucción if, sin requerir una declaración/cast y check por separado:

if (obj1 is int integerValue)
{
    integerValue++;
}

Esto parece bastante decepcionante para un solo elenco como este, pero realmente brilla cuando tienes muchos tipos posibles que entran en tu rutina. La siguiente es la vieja manera de evitar el casting dos veces:

Button button = obj1 as Button;
if (button != null)
{
    // do stuff...
    return;
}
TextBox text = obj1 as TextBox;
if (text != null)
{
    // do stuff...
    return;
}
Label label = obj1 as Label;
if (label != null)
{
    // do stuff...
    return;
}
// ... and so on

Siempre me ha molestado trabajar para reducir este código tanto como sea posible, así como evitar los casts duplicados del mismo objeto. Lo anterior está muy bien comprimido con el patrón que coincide con lo siguiente:

switch (obj1)
{
    case Button button:
        // do stuff...
        break;
    case TextBox text:
        // do stuff...
        break;
    case Label label:
        // do stuff...
        break;
    // and so on...
}

EDITAR: Se actualizó el nuevo método más largo para usar un switch según el comentario de Palec.

 8
Author: JoelC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-26 19:56:38

Prefiero es

Dicho esto, si estás usando es, es probable que no usando la herencia correctamente.

Supongamos que esa Persona : Entidad, y ese Animal : Entidad. Feed es un método virtual en Entidad (para hacer feliz a Neil)

class Person
{
  // A Person should be able to Feed
  // another Entity, but they way he feeds
  // each is different
  public override void Feed( Entity e )
  {
    if( e is Person )
    {
      // feed me
    }
    else if( e is Animal )
    {
      // ruff
    }
  }
}

Más bien

class Person
{
  public override void Feed( Person p )
  {
    // feed the person
  }
  public override void Feed( Animal a )
  {
    // feed the animal
  }
}
 7
Author: bobobobo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-23 14:10:23

Creo que el último también mira la herencia (por ejemplo, Perro es Animal == verdadero), que es mejor en la mayoría de los casos.

 5
Author: StriplingWarrior,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-11 19:14:00

Depende de lo que estoy haciendo. Si necesito un valor bool (digamos, para determinar si voy a lanzar a un int), usaré is. Si realmente necesito el tipo por alguna razón (digamos, para pasar a algún otro método) usaré GetType().

 2
Author: AllenG,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-11 19:20:16

El último es más limpio, más obvio, y también comprueba los subtipos. Los otros no comprueban si hay polimorfismo.

 0
Author: thecoop,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-11 19:16:12

Utilizado para obtener el Sistema.Escriba objeto para un tipo. Un tipo de expresión toma la siguiente forma:

System.Type type = typeof(int);

Example:

    public class ExampleClass
    {
       public int sampleMember;
       public void SampleMethod() {}

       static void Main()
       {
          Type t = typeof(ExampleClass);
          // Alternatively, you could use
          // ExampleClass obj = new ExampleClass();
          // Type t = obj.GetType();

          Console.WriteLine("Methods:");
          System.Reflection.MethodInfo[] methodInfo = t.GetMethods();

          foreach (System.Reflection.MethodInfo mInfo in methodInfo)
             Console.WriteLine(mInfo.ToString());

          Console.WriteLine("Members:");
          System.Reflection.MemberInfo[] memberInfo = t.GetMembers();

          foreach (System.Reflection.MemberInfo mInfo in memberInfo)
             Console.WriteLine(mInfo.ToString());
       }
    }
    /*
     Output:
        Methods:
        Void SampleMethod()
        System.String ToString()
        Boolean Equals(System.Object)
        Int32 GetHashCode()
        System.Type GetType()
        Members:
        Void SampleMethod()
        System.String ToString()
        Boolean Equals(System.Object)
        Int32 GetHashCode()
        System.Type GetType()
        Void .ctor()
        Int32 sampleMember
    */

Este ejemplo utiliza el método GetType para determinar el tipo que se utiliza para contener el resultado de un cálculo numérico. Esto depende de los requisitos de almacenamiento del número resultante.

    class GetTypeTest
    {
        static void Main()
        {
            int radius = 3;
            Console.WriteLine("Area = {0}", radius * radius * Math.PI);
            Console.WriteLine("The type is {0}",
                              (radius * radius * Math.PI).GetType()
            );
        }
    }
    /*
    Output:
    Area = 28.2743338823081
    The type is System.Double
    */
 0
Author: Muhammad Awais,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-14 07:52:21
if (c is UserControl) c.Enabled = enable;
 -3
Author: Paulos02,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-06 13:18:46

Puede usar el operador" typeof () "en C# pero necesita llamar al espacio de nombres usando System.IO; Debe usar la palabra clave" is " si desea verificar un tipo.

 -5
Author: androidrill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-07 07:01:41

Prueba de rendimiento typeof () vs GetType ():

using System;
namespace ConsoleApplication1
    {
    class Program
    {
        enum TestEnum { E1, E2, E3 }
        static void Main(string[] args)
        {
            {
                var start = DateTime.UtcNow;
                for (var i = 0; i < 1000000000; i++)
                    Test1(TestEnum.E2);
                Console.WriteLine(DateTime.UtcNow - start);
            }
            {
                var start = DateTime.UtcNow;
                for (var i = 0; i < 1000000000; i++)
                    Test2(TestEnum.E2);
                Console.WriteLine(DateTime.UtcNow - start);
            }
            Console.ReadLine();
        }
        static Type Test1<T>(T value) => typeof(T);
        static Type Test2(object value) => value.GetType();
    }
}

Resultados en modo de depuración:

00:00:08.4096636
00:00:10.8570657

Resultados en el modo de liberación:

00:00:02.3799048
00:00:07.1797128
 -5
Author: Alexander Vasilyev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-03 13:58:53