2011-03-28 15 views
40

Usando la reflexión, intento encontrar el conjunto de tipos que heredan de una clase base determinada. No pasó mucho tiempo para descubrir tipos simples, pero estoy perplejo cuando se trata de genéricos.Uso de IsAssignableFrom con tipos genéricos 'abiertos'

Para este fragmento de código, el primer IsAssignableFrom devuelve verdadero, pero el segundo devuelve falso. Y, sin embargo, la tarea final compila muy bien.

class class1 { } 
class class2 : class1 { } 
class generic1<T> { } 
class generic2<T> : generic1<T> { } 

class Program 
{ 
    static void Main(string[] args) 
    { 
     Type c1 = typeof(class1); 
     Type c2 = typeof(class2); 
     Console.WriteLine("c1.IsAssignableFrom(c2): {0}", c1.IsAssignableFrom(c2)); 

     Type g1 = typeof(generic1<>); 
     Type g2 = typeof(generic2<>); 
     Console.WriteLine("g1.IsAssignableFrom(g2): {0}", g1.IsAssignableFrom(g2)); 

     generic1<class1> cc = new generic2<class1>(); 
    } 
} 

Entonces, ¿cómo puedo determinar en tiempo de ejecución si una definición de tipo genérico se deriva de otra?

+2

la asignación final sólo implica 'gen ric2' ... –

+0

posible duplicado de [Cómo detectar si el tipo es otro tipo genérico] (http://stackoverflow.com/questions/74616/how-to-detect-if-type-is-another-generic-type) –

+0

@Daniel Hilgarth - ¡Gracias! Me perdí eso cuando estaba limpiando el código de muestra antes de publicarlo. Todavía se compila cuando la asignación es genérica1 cc = new generic2 (); – ThatBlairGuy

Respuesta

64

Desde el answer to another question:

public static bool IsAssignableToGenericType(Type givenType, Type genericType) 
{ 
    var interfaceTypes = givenType.GetInterfaces(); 

    foreach (var it in interfaceTypes) 
    { 
     if (it.IsGenericType && it.GetGenericTypeDefinition() == genericType) 
      return true; 
    } 

    if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType) 
     return true; 

    Type baseType = givenType.BaseType; 
    if (baseType == null) return false; 

    return IsAssignableToGenericType(baseType, genericType); 
} 

(Si te gusta la respuesta por favor upvote la respuesta ligado ya que el código no es mía.)

+0

Gracias! Esto hace exactamente lo que estaba buscando. No había notado esa otra pregunta cuando miré antes. – ThatBlairGuy

+2

En el siguiente caso, este método podría ser incorrecto: IsAssignableToGenericType (typeof (A ), typeof (A <>); // return false –

+0

Sin embargo, esta respuesta es genial, sé útil cuando estés usando esto. Lo usé, hice funcionar la expresión bool y luego no pude hacer nada porque podía lanzar mi objeto para usarlo ... Tenía que introducir una interfaz que simplemente podría verificar con 'is' ... – Jaap

8

El código exacto informados no devuelve resultados sorprendentes.

Esto dice "falsa":

Type g1 = typeof(generic1<>); 
Type g2 = typeof(generic2<>); 
Console.WriteLine("g1.IsAssignableFrom(g2): {0}", g1.IsAssignableFrom(g2)); 

Esto dice "verdadero":

Type g1 = typeof(generic1<class1>); 
Type g2 = typeof(generic2<class1>); 
Console.WriteLine("g1.IsAssignableFrom(g2): {0}", g1.IsAssignableFrom(g2)); 

La diferencia es que los tipos genéricos abiertos no pueden tener instancias, por lo que uno no es "asignable" a la otro.

Desde el docs:

devoluciones true si c y la corriente Type representan el mismo tipo, o si la corriente Type es en la jerarquía herencia de c, o si la corriente Type es una interfaz que implementa c, o si c es un parámetro de tipo genérico y elactual 0 Type representa una de las restricciones de c. false si ninguna de estas condiciones son verdaderas, o si c es null.

En este caso, claramente ninguna de estas condiciones es verdadera. Y hay una nota extra:

Una definición de tipo genérico no es asignable de una cerrada construida tipo. Es decir, no puede asignar tipo construido cerrado MyGenericList<int> (MyGenericList(Of Integer) en Visual Basic) a una variable del tipo MyGenericList<T>.

+1

¡Buena explicación! –

2

En el siguiente caso, utilice el método Konrad Rudolph siempre podría estar mal, como: IsAssignableToGenericType (typeof (A), typeof (A <>)); // return false

Creo que aquí hay un mejor responder

public static bool IsAssignableFrom(Type extendType, Type baseType) 
{ 
    while (!baseType.IsAssignableFrom(extendType)) 
    { 
     if (extendType.Equals(typeof(object))) 
     { 
      return false; 
     } 
     if (extendType.IsGenericType && !extendType.IsGenericTypeDefinition) 
     { 
      extendType = extendType.GetGenericTypeDefinition(); 
     } 
     else 
     { 
      extendType = extendType.BaseType; 
     } 
    } 
    return true; 
} 

el caso de prueba, ver Using IsAssignableFrom with C# generics para el detalle

using System; 

/** 
* Sam Sha - yCoder.com 
* 
* */ 
namespace Test2 
{ 
    class MainClass 
    { 
     public static void Main (string[] args) 
     { 
      string a = "ycoder"; 
      Console.WriteLine(a is object); 
      A aa = new A(); 
      //Console.WriteLine(aa is A<>);//con't write code like this 
      typeof(A<>).IsAssignableFrom(aa.GetType());//return false 

      Trace(typeof(object).IsAssignableFrom(typeof(string)));//true 
      Trace(typeof(A<>).IsAssignableFrom(typeof(A)));//false 

      AAA aaa = new AAA(); 
      Trace("Use IsTypeOf:"); 
      Trace(IsTypeOf(aaa, typeof(A<>))); 
      Trace(IsTypeOf(aaa, typeof(AA))); 
      Trace(IsTypeOf(aaa, typeof(AAA<>))); 

      Trace("Use IsAssignableFrom from stackoverflow - not right:"); 
      Trace(IsAssignableFrom(typeof(A), typeof(A<>))); // error 
      Trace(IsAssignableFrom(typeof(AA), typeof(A<>))); 
      Trace(IsAssignableFrom(typeof(AAA), typeof(A<>))); 

      Trace("Use IsAssignableToGenericType:"); 
      Trace(IsAssignableToGenericType(typeof(A), typeof(A<>))); 
      Trace(IsAssignableToGenericType(typeof(AA), typeof(A<>))); 
      Trace(IsAssignableToGenericType(typeof(AAA), typeof(A<>))); 
     } 

     static void Trace(object log){ 
       Console.WriteLine(log); 
     } 

     public static bool IsTypeOf(Object o, Type baseType) 
     { 
      if (o == null || baseType == null) 
      { 
       return false; 
      } 
      bool result = baseType.IsInstanceOfType(o); 
      if (result) 
      { 
       return result; 
      } 
      return IsAssignableFrom(o.GetType(), baseType); 
     } 

     public static bool IsAssignableFrom(Type extendType, Type baseType) 
     { 
      while (!baseType.IsAssignableFrom(extendType)) 
      { 
       if (extendType.Equals(typeof(object))) 
       { 
        return false; 
       } 
       if (extendType.IsGenericType && !extendType.IsGenericTypeDefinition) 
       { 
        extendType = extendType.GetGenericTypeDefinition(); 
       } 
       else 
       { 
        extendType = extendType.BaseType; 
       } 
      } 
      return true; 
     } 

     //from stackoverflow - not good enough 
     public static bool IsAssignableToGenericType(Type givenType, Type genericType) { 
      var interfaceTypes = givenType.GetInterfaces(); 

      foreach (var it in interfaceTypes) 
       if (it.IsGenericType) 
        if (it.GetGenericTypeDefinition() == genericType) return true; 

      Type baseType = givenType.BaseType; 
      if (baseType == null) return false; 

      return baseType.IsGenericType && 
       baseType.GetGenericTypeDefinition() == genericType || 
       IsAssignableToGenericType(baseType, genericType); 
     } 
    } 

    class A{} 
    class AA : A{} 
    class AAA : AA{} 
} 
+2

Nota: esta respuesta no maneja interfaces genéricas. – phloopy

Cuestiones relacionadas