2010-03-30 14 views
5

Dar una clase base Base, quiero escribir un Método de ensayo, así:Cómo averiguar si un tipo de objeto implementa IEnumerable <X> donde X se deriva de Base utilizando Reflexión

private static bool Test(IEnumerable enumerable) 
{ 
... 
} 

tal que prueba devuelve verdadero si el tipo de o implementa cualquier interfaz de IEnumerable<X> donde X deriva de Base, por lo que si me gustaría hacer esto:

public static IEnumerable<string> Convert(IEnumerable enumerable) 
{ 
    if (Test(enumerable)) 
    { 
     return enumerable.Cast<Base>().Select(b => b.SomePropertyThatIsString); 
    } 

    return enumerable.Cast<object>().Select(o => o.ToString()); 
} 

... que sería hacer lo correcto, usando Reflecti en. Estoy seguro de que es una cuestión de recorrer todas las interfaces del tipo para encontrar la primera que coincida con los requisitos, pero estoy teniendo dificultades para encontrar el genérico IEnumerable<> entre ellos.

Por supuesto, podría considerar esto:

public static IEnumerable<string> Convert(IEnumerable enumerable) 
{ 
    return enumerable.Cast<object>().Select(o => o is Base ? ((Base)o).SomePropertyThatIsString : o.ToString()); 
} 

... pero pensar en él como un experimento mental.

+0

Sus fragmentos de código no tienen sentido, ¿de dónde viene "o"? –

+12

Quizás deberías leer sobre las expresiones lambda. –

Respuesta

16

También es posible usar una consulta LINQ que podría tener este aspecto.

public static bool ImplementsBaseType(IEnumerable objects) 
{ 
    int found = (from i in objects.GetType().GetInterfaces() 
       where i.IsGenericType && 
         i.GetGenericTypeDefinition() == typeof(IEnumerable<>) && 
         typeof(MyBaseClass).IsAssignableFrom(i.GetGenericArguments()[0]) 
       select i).Count(); 

    return (found > 0); 
} 

Este código asume las siguientes instrucciones using:

using System; 
using System.Collections; 
using System.Collections.Generic; 
using System.Linq; 

Dado que este es sólo un experimento mental. Aquí hay otra implementación como método de extensión.

public static class ConversionAssistants 
{ 
    public static bool GenericImplementsType(this IEnumerable objects, Type baseType) 
    { 
     foreach (Type type in objects.GetType().GetInterfaces()) 
     { 
      if (type.IsGenericType) 
      { 
       if (type.GetGenericTypeDefinition() == typeof(IEnumerable<>)) 
       { 
        if (baseType.IsAssignableFrom(type.GetGenericArguments()[0])) 
         return true; 
       } 
      } 
     } 
     return false; 
    } 
} 
+2

en lugar de arg.BaseType == typeof (MyBaseClass), podría decir typeof (MyBaseClass) .IsAssignableFrom (arg), ¿verdad? –

+0

@Dave: Sí, eso también funciona. – VoidDweller

+1

IsAssignableFrom' es mejor, porque '==' solo funcionará si el tipo hereda * directamente * de MyBaseClass –

0

Puede utilizar el Type.FindInterfaces para filtrar todas las interfaces de los implementos IEnumerable<> tipo y comprobar los parámetros genéricos (a través de Type.GetGenericArguments) en cada uno de ellos para ver si es o Base hereda de Base.

actualización: He aquí algunos ejemplos de código:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Reflection; 

namespace ConsoleApplication1 
{ 
    class Base 
    { 
     string Prop { get; set;} 
    } 

    class A : Base 
    { 
    } 

    class Test : List<A> 
    { 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      try 
      { 
       Test test = new Test(); 
       Type myType = test.GetType(); 

       //string filterCriteria = "IEnumerable`1"; 
       Type typeA = Type.GetType("ConsoleApplication1.A"); 
       string filterCriteria = "System.Collections.Generic.IEnumerable`1[[" + 
             typeA.AssemblyQualifiedName + 
             "]]"; 

       // Specify the TypeFilter delegate that compares the 
       // interfaces against filter criteria. 
       TypeFilter myFilter = new TypeFilter(MyInterfaceFilter); 
       Type[] myInterfaces = myType.FindInterfaces(myFilter, 
        filterCriteria); 
       if (myInterfaces.Length > 0) 
       { 
        Console.WriteLine("\n{0} implements the interface {1}.", 
         myType, filterCriteria); 
        for (int j = 0; j < myInterfaces.Length; j++) 
         Console.WriteLine("Interfaces supported: {0}.", 
          myInterfaces[j].ToString()); 
       } 
       else 
        Console.WriteLine(
         "\n{0} does not implement the interface {1}.", 
         myType, filterCriteria); 
      } 
      catch (ArgumentNullException e) 
      { 
       Console.WriteLine("ArgumentNullException: " + e.Message); 
      } 
      catch (TargetInvocationException e) 
      { 
       Console.WriteLine("TargetInvocationException: " + e.Message); 
      } 
      catch (Exception e) 
      { 
       Console.WriteLine("Exception: " + e.Message); 
      } 
     } 

     public static bool MyInterfaceFilter(Type typeObj, Object criteriaObj) 
     { 
      // This will be true, if criteria is 
      // System.Collections.Generic.IEnumerable`1[[ConsoleApplication1.A, ConsoleApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] 
      if (typeObj.FullName == criteriaObj.ToString()) 
       return true; 
      // This will be true, if criteria is 
      // IEnumerable`1 
      // You will still need to check the generic parameters on the original type 
       // (generic parameters are not exposed on Type instances for interfaces 
      else if (typeObj.Name == criteriaObj.ToString()) 
       return true; 
      else 
       return false; 
     } 
    } 
} 
+0

Eso es lo que pensé, pero ¿cómo puedo comprobar si una interfaz es una instancia de la interfaz genérica IEnumerable <>? –

-2

He escrito recientemente algún código que necesita iterar sobre cualquier colección.

Como una aplicación .NET heredada, ¡ni siquiera tenía genéricos disponibles para mí!

He aquí un extracto:

var t = objects.GetType(); // to be compatible with the question 

bool isIEnumerable = false; 
foreach (var i in t.GetInterfaces()) 
{ 
    if (i == typeof(IEnumerable)) 
    { 
     isIEnumerable = true; 
     break; 
    } 
} 

He encontrado que incluso las clases de colección .NET 1.1 como SqlParameterCollection eran IEnumerable.
También atrapa colecciones genéricas como la Lista <> ya que también son IEnumerables.

Espero que esto ayude a alguien.

+0

La pregunta es específica de Generics. Además, puedes simplemente hacer 'objects is IEnumerable' o' objects as IEnumerable' para probar si es enumerable. Tu solución es bastante intrincada. –

Cuestiones relacionadas