2009-06-11 9 views
109

Me gustaría realizar una prueba si un objeto es de un tipo genérico. He intentado lo siguiente sin éxito:Comprobando si el objeto es de tipo genérico en C#

public bool Test() 
{ 
    List<int> list = new List<int>(); 
    return list.GetType() == typeof(List<>); 
} 

¿Qué estoy haciendo mal y cómo se realiza esta prueba?

Respuesta

153

Si desea comprobar si se trata de una instancia de un tipo genérico:

return list.GetType().IsGenericType; 

Si desea comprobar si se trata de un genérico List<T>:

return list.GetType().GetGenericTypeDefinition() == typeof(List<>); 

Como Jon señala, esto cheques la equivalencia de tipo exacto. Devolver false no significa necesariamente list is List<T> devuelve false (es decir, el objeto no se puede asignar a una variable List<T>).

+2

Sin embargo, eso no detectará los subtipos. Ver mi respuesta También es mucho más difícil para las interfaces :( –

1
return list.GetType().IsGenericType; 
+2

"Es de" no "es un" – Gregory

+3

Es correcto para una pregunta diferente. Para esta pregunta, es incorrecta, ya que solo soluciona (significativamente menos) la mitad del problema. – Groxx

+1

Stan En realidad, la respuesta de R responde la pregunta tal como se planteó, pero lo que el OP realmente quiso decir fue "Probando si el objeto es de un tipo genérico * particular * en C#", por lo que esta respuesta es incompleta. – yoyo

68

que suponer que usted no sólo quiere saber si el tipo es genérico, pero si un objeto es una instancia de un tipo genérico en particular, sin conocer los argumentos de tipo.

No es terriblemente simple, desafortunadamente. No es tan malo si el tipo genérico es una clase (como lo es en este caso), pero es más difícil para las interfaces. Aquí está el código para una clase:

using System; 
using System.Collections.Generic; 
using System.Reflection; 

class Test 
{ 
    static bool IsInstanceOfGenericType(Type genericType, object instance) 
    { 
     Type type = instance.GetType(); 
     while (type != null) 
     { 
      if (type.IsGenericType && 
       type.GetGenericTypeDefinition() == genericType) 
      { 
       return true; 
      } 
      type = type.BaseType; 
     } 
     return false; 
    } 

    static void Main(string[] args) 
    { 
     // True 
     Console.WriteLine(IsInstanceOfGenericType(typeof(List<>), 
                new List<string>())); 
     // False 
     Console.WriteLine(IsInstanceOfGenericType(typeof(List<>), 
                new string[0])); 
     // True 
     Console.WriteLine(IsInstanceOfGenericType(typeof(List<>), 
                new SubList())); 
     // True 
     Console.WriteLine(IsInstanceOfGenericType(typeof(List<>), 
                new SubList<int>())); 
    } 

    class SubList : List<string> 
    { 
    } 

    class SubList<T> : List<T> 
    { 
    } 
} 

EDIT: Como se señaló en los comentarios, esto puede funcionar para interfaces:

foreach (var i in type.GetInterfaces()) 
{ 
    if (i.IsGenericType && i.GetGenericTypeDefinition() == genericType) 
    { 
     return true; 
    } 
} 

Tengo la sospecha de que puede haber algunos casos extremos difíciles de todo esto, pero No puedo encontrar uno por el que falla por ahora.

+0

Acabo de descubrir un problema con esto. Solo desciende en una sola línea de herencia. Si, en el camino, tiene una base con una clase base * y * la interfaz que está buscando, esto solo se aplica a la ruta de clase. – Groxx

+0

@Groxx: cierto. Acabo de descubrir que sí lo menciono en la respuesta: "No está mal si el tipo genérico es una clase (como lo es en este caso) pero es más difícil para las interfaces. Aquí está el código para una clase" –

+0

Ah , tienes razón. Me perdí la parte "por una clase". Gracias por la cantidad de código, por cierto, ha sido útil :) – Groxx

5

Puede utilizar el código más corto usando althougth dinámico que esto puede ser más lenta que la pura reflexión:

public static class Extension 
{ 
    public static bool IsGenericList(this object o) 
    { 
     return IsGeneric((dynamic)o); 
    } 

    public static bool IsGeneric<T>(List<T> o) 
    { 
     return true; 
    } 

    public static bool IsGeneric(object o) 
    { 
     return false; 
    } 
} 



var l = new List<int>(); 
l.IsGenericList().Should().BeTrue(); 

var o = new object(); 
o.IsGenericList().Should().BeFalse(); 
3

Estos son mis dos métodos favoritos de extensión que cubren la mayoría de los casos extremos de comprobación de tipos genéricos:

Funciona con :

  • múltiples (genéricos) interfaces de
  • clases
  • múltiple (genérico) de base
  • tiene una sobrecarga que será del tipo genérico de concreto 'hacia fuera' si devuelve verdadero (ver prueba de unidad para las muestras):

    public static bool IsOfGenericType(this Type typeToCheck, Type genericType) 
    { 
        Type concreteType; 
        return typeToCheck.IsOfGenericType(genericType, out concreteType); 
    } 
    
    public static bool IsOfGenericType(this Type typeToCheck, Type genericType, out Type concreteGenericType) 
    { 
        while (true) 
        { 
         concreteGenericType = null; 
    
         if (genericType == null) 
          throw new ArgumentNullException(nameof(genericType)); 
    
         if (!genericType.IsGenericTypeDefinition) 
          throw new ArgumentException("The definition needs to be a GenericTypeDefinition", nameof(genericType)); 
    
         if (typeToCheck == null || typeToCheck == typeof(object)) 
          return false; 
    
         if (typeToCheck == genericType) 
         { 
          concreteGenericType = typeToCheck; 
          return true; 
         } 
    
         if ((typeToCheck.IsGenericType ? typeToCheck.GetGenericTypeDefinition() : typeToCheck) == genericType) 
         { 
          concreteGenericType = typeToCheck; 
          return true; 
         } 
    
         if (genericType.IsInterface) 
          foreach (var i in typeToCheck.GetInterfaces()) 
           if (i.IsOfGenericType(genericType, out concreteGenericType)) 
            return true; 
    
         typeToCheck = typeToCheck.BaseType; 
        } 
    } 
    

Aquí está una prueba para demostrar la funcionalidad (básico):

[Test] 
    public void SimpleGenericInterfaces() 
    { 
     Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IEnumerable<>))); 
     Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IQueryable<>))); 

     Type concreteType; 
     Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IEnumerable<>), out concreteType)); 
     Assert.AreEqual(typeof(IEnumerable<string>), concreteType); 

     Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IQueryable<>), out concreteType)); 
     Assert.AreEqual(typeof(IQueryable<string>), concreteType); 


    } 
Cuestiones relacionadas