2009-06-04 20 views
15

Quiero escribir un método que use Reflection para indicar si un Type dado implementa IList<T>. Por ejemplo:¿Cómo puedo saber si un Type implementa IList <>?

IsGenericList(typeof(int))      // should return false 
IsGenericList(typeof(ArrayList))     // should return false 
IsGenericList(typeof(IList<int>))    // should return true 
IsGenericList(typeof(List<int>))     // should return true 
IsGenericList(typeof(ObservableCollection<int>)) // should return true 

En mi uso, que se puede suponer que el tipo siempre será una instancia de tipo genérico (o algo que no es genérica en absoluto).

Desafortunadamente, esto no es tan fácil como debería ser. La solución obvia:

public bool IsGenericList(Type type) 
{ 
    return typeof(IList<>).IsAssignableFrom(type); 
} 

no funciona; siempre devuelve falso. Aparentemente, los tipos genéricos no instanciados como IList<> no implementan IsAssignableFrom la forma en que yo esperaba: IList<> no se puede asignar desde List<T>.

También he intentado esto:

public bool IsGenericList(Type type) 
{ 
    if (!type.IsGenericType) 
     return false; 
    var genericTypeDefinition = type.GetGenericTypeDefinition(); 
    return typeof(List<>).IsAssignableFrom(genericTypeDefinition); 
} 

es decir, girar type en su no una instancia genérica, como IList<int> ->IList<>, y luego tratar IsAssignableFrom nuevo. Que devolverá verdadero si el tipo es una instancia IList<T> como IList<int>, IList<object>, etc, pero que devuelve false para clases que implementan IList<T> como List<int>, ObservableCollection<double>, etc., por lo que aparentemente IList<> no es asignable a partir List<>. Nuevamente, no es lo que esperaría.

¿Cómo hago para escribir IsGenericList y hacer que funcione como en los ejemplos anteriores?

Respuesta

23

De hecho, no puede tener una instancia de una definición de tipo genérico. Por lo tanto, el método IsAssignableFrom() funciona como se esperaba. Para lograr lo que quiere, haga lo siguiente:

public bool IsGenericList(Type type) 
{ 
    if (type == null) { 
     throw new ArgumentNullException("type"); 
    } 
    foreach (Type @interface in type.GetInterfaces()) { 
     if (@interface.IsGenericType) { 
      if (@interface.GetGenericTypeDefinition() == typeof(ICollection<>)) { 
       // if needed, you can also return the type used as generic argument 
       return true; 
      } 
     } 
    } 
    return false; 
} 

Sólo por curiosidad, ¿para qué necesita esto?

+1

+1 - Esto funciona, y es la única solución que hay actualmente: me di cuenta de que tenía esto después de publicar lo mismo. –

+0

Nunca vi @ para usar palabras clave como nombres de variables en ningún código de producción. – VVS

+0

Buena solución. :) Sin embargo, un pequeño punto: el uso de los símbolos @ generalmente no es una práctica recomendada. (Simplemente podría llamar a la variable 'i' o' curInterface' en su lugar para resolver el choque de nombres.) – Noldorin

-3

Utilice la "is" operador:

El es el operador se utiliza para comprobar si el tipo de tiempo de ejecución de un objeto es compatible con un tipo dado.

+0

No tengo una instancia. Tengo el tipo. –

+0

Esto no funcionará con IList <>, solo con IList , etc. Debe tener un tipo concreto para que "is" funcione ... –

+0

Necesita una instancia para 'is'. El OP preguntó cómo hacer el cheque usando un 'Tipo'. – Oliver

1

Lucero/Reed Copsey ambos tienen la solución correcta ahora. Sólo para que sea más concisa, aquí está en forma LINQified:

var isGenericList = type.GetInterfaces().Any(t => t.IsGenericType && 
    t.GetGenericTypeDefinition() == typeof(IList<>)); 
+0

No funcionará, ya que GetInterfaces() no devolverá las definiciones de tipo genérico, solo los tipos genéricos (como IList ). – Lucero

+0

Probado - no funciona (fue mi primer intento, también). –

+0

Sí, entonces no es así. Lo he actualizado y parece funcionar ahora sin embargo. – Noldorin

1

Esto pasa sus pruebas ...

public static bool IsGenericList(Type type) 
{ 
    return type.Name == "IList`1" || type.GetInterface("IList`1") != null; 
} 
+0

tipo no tiene que ser genérico para implementar una interfaz genérica. Puede tener esto: class IntList: object, IList {....} que no hace que IntList sea genérico. – Lucero

+0

La comprobación IsGeneric no es necesaria en absoluto – tanascius

0

Ha intentado llamar Type.GetInterface()? No está del todo claro con la ayuda, pero creo que buscará interfaces implementadas por tipos básicos, además del tipo en sí. De lo contrario, siempre puede recorrer el Type.BaseType y llamar al GetInterface() nuevamente.

+0

GetInterface solo funciona si conoce el argumento genérico específico, pero no funcionará para IList . –

2

El uso de este: http://msdn.microsoft.com/en-us/library/system.type.findinterfaces.aspx

yo probamos este:

public class Test : IList<string> 
{ 
//implementation left out... 
} 

class Program 
    { 
     static void Main(string[] args) 
     { 
      Test t = new Test(); 
      TypeFilter myFilter = new TypeFilter(MyInterfaceFilter); 

      Type type = t.GetType(); 
      Type[] x = type.FindInterfaces(myFilter, "System.Collections.Generic.IList"); 
      Console.WriteLine(x.Length); 

     } 

     public static bool MyInterfaceFilter(Type typeObj, Object criteriaObj) 
     { 
      if (typeObj.ToString().Contains(criteriaObj.ToString())) 
       return true; 
      else 
       return false; 
     } 
    } 
+4

Usar un nombre no fuerte para identificar un tipo es muy inseguro. Cualquiera puede crear un tipo con ese nombre, no significa que sea del tipo que desea probar. – Lucero

+0

Es cierto, simplemente podría reemplazarlo con esto: Escriba [] x = type.FindInterfaces (myFilter, typeof (IList <>).Nombre completo); – Irwin

11

yo también quieren probar si un tipo implementa IList<T> por alguna T. hice el cambio obvio a Lucero's answer pero causó una subtle bug no presente en la respuesta original. Aquí está mi edición final:

/// <summary> 
    /// Test if a type derives from IList of T, for any T. 
    /// </summary> 
    public bool TestIfGenericList(Type type) 
    { 
     if (type == null) 
     { 
      throw new ArgumentNullException("type"); 
     } 

     var interfaceTest = new Predicate<Type>(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IList<>)); 

     return interfaceTest(type) || type.GetInterfaces().Any(i => interfaceTest(i)); 
    } 
+2

Gracias, eso es mejor que la respuesta aceptada, ya que funciona para 'IList '. –

Cuestiones relacionadas