2010-08-23 17 views
6

Tengo un montón de Colecciones IEnumerable cuyo número exacto y tipos están sujetos a cambios frecuentes (debido a la generación automática de código).Cómo invocar System.Linq.Enumerable.Count <> en IEnumerable <T> utilizando Reflection?

se ve algo como esto:

public class MyCollections { 
    public System.Collections.Generic.IEnumerable<SomeType> SomeTypeCollection; 
    public System.Collections.Generic.IEnumerable<OtherType> OtherTypeCollection; 
    ... 

En tiempo de ejecución que quiero para determinar cada tipo y es contar sin tener que volver a escribir el código después de cada generación de código. Así que estoy buscando un enfoque genérico utilizando la reflexión. El resultado que estoy buscando es algo así como:

MyType: 23 
OtherType: 42 

Mi problema es que no puedo entender cómo invocar correctamente el método Count. Aquí es lo que tengo hasta ahora:

 // Handle to the Count method of System.Linq.Enumerable 
     MethodInfo countMethodInfo = typeof(System.Linq.Enumerable).GetMethod("Count", new Type[] { typeof(IEnumerable<>) }); 

     PropertyInfo[] properties = typeof(MyCollections).GetProperties(); 
     foreach (PropertyInfo property in properties) 
     { 
      Type propertyType = property.PropertyType; 
      if (propertyType.IsGenericType) 
      { 
       Type genericType = propertyType.GetGenericTypeDefinition(); 
       if (genericType == typeof(IEnumerable<>)) 
       { 
        // access the collection property 
        object collection = property.GetValue(someInstanceOfMyCollections, null); 

        // access the type of the generic collection 
        Type genericArgument = propertyType.GetGenericArguments()[0]; 

        // make a generic method call for System.Linq.Enumerable.Count<> for the type of this collection 
        MethodInfo localCountMethodInfo = countMethodInfo.MakeGenericMethod(genericArgument); 

        // invoke Count method (this fails) 
        object count = localCountMethodInfo.Invoke(collection, null); 

        System.Diagnostics.Debug.WriteLine("{0}: {1}", genericArgument.Name, count); 
       } 
      } 
     } 
+0

¿Es "MyCollections" un tipo de variable/campo/propiedad? Parece que lo estás usando como los dos. –

+0

Su recuento MethodInfo-reference es nulo. Arregle eso y probablemente el código funcione. –

+0

Lo sentimos, el ejemplo dado no es exacto. Lo he arreglado – mbi

Respuesta

3

Si insiste en hacerlo de la manera difícil; p

Cambios:

  • como se obtiene countMethodInfo de un método genérico
  • los argumentos para invocar
Código

(nota obj es mi instancia de MyCollections) :

MethodInfo countMethodInfo = typeof (System.Linq.Enumerable).GetMethods().Single(
     method => method.Name == "Count" && method.IsStatic && method.GetParameters().Length == 1); 

    PropertyInfo[] properties = typeof(MyCollections).GetProperties(); 
    foreach (PropertyInfo property in properties) 
    { 
     Type propertyType = property.PropertyType; 
     if (propertyType.IsGenericType) 
     { 
      Type genericType = propertyType.GetGenericTypeDefinition(); 
      if (genericType == typeof(IEnumerable<>)) 
      { 
       // access the collection property 
       object collection = property.GetValue(obj, null); 

       // access the type of the generic collection 
       Type genericArgument = propertyType.GetGenericArguments()[0]; 

       // make a generic method call for System.Linq.Enumerable.Count<> for the type of this collection 
       MethodInfo localCountMethodInfo = countMethodInfo.MakeGenericMethod(genericArgument); 

       // invoke Count method (this fails) 
       object count = localCountMethodInfo.Invoke(null, new object[] {collection}); 

       System.Diagnostics.Debug.WriteLine("{0}: {1}", genericArgument.Name, count); 
      } 
     } 
    } 
+0

¡Muchas gracias! Después de examinar tu respuesta, de repente entendí mi error. De hecho, solo los argumentos a Invoke tuvieron que ajustarse (¡por supuesto!) Y luego toda mi implementación original funcionó como se esperaba. Incluso su enfoque de Linq para recuperar countMethodInfo se ve sexy, se siente un poco complicado para mí ;-) – mbi

+0

@embee - cuando probé su código original, obtuve 'null' para el methodinfo ... –

+0

funciona bien para silverlight/teléfono con ventana – mbi

2

que va a implicar algún MakeGenericMethod - y mucha reflexión general. En lo personal, yo estaría tentado a simplificar zanjando los genéricos en este caso:

public static int Count(IEnumerable data) { 
    ICollection list = data as ICollection; 
    if(list != null) return list.Count; 
    int count = 0; 
    IEnumerator iter = data.GetEnumerator(); 
    using(iter as IDisposable) { 
     while(iter.MoveNext()) count++; 
    } 
    return count; 
} 

Puedes lanzar a la no genérico IEnumerable trivial, incluso si ir a buscar a través de la reflexión.

+0

Por supuesto, esta es una buena idea. Pero estoy buscando la solución a mi falla en MakeGenericMethod. – mbi

+0

¿No podemos simplemente 'foreach' sobre' data', o tenemos que tratar con el enumerador de forma explícita? – AakashM

+0

@embee - 2 segundos, lo veré ... –

1
var count = System.Linq.Enumerable.Count(theCollection); 

Editar: Usted dice que es generada embargo, por lo que no sólo puede generar el inmueble con llamadas a Count()?

public class MyCollections 
{ 
    public System.Collections.Generic.IEnumerable<SomeType> SomeTypeCollection; 
    public System.Collections.Generic.IEnumerable<OtherType> OtherTypeCollection; 

    public int CountSomeTypeCollection 
    { 
     get { return this.SomeTypeCollection.Count(); } 
    } 

    ... 
+0

Disculpa, extrañé el hecho de que no sabes 'T' en tiempo de compilación. –

+0

MyCollections se genera automáticamente y se sobrescribe con bastante frecuencia. Es por eso que tengo que usar la reflexión desde el exterior. Sé que es complicado, pero debería ser posible ... – mbi

1

Por ahora, la pregunta ha sido contestada, pero me gustaría presentarles una recortada — y creo que, más bien trivial versión — de la "llamar a un método de extensión genérico", que puede ser utilizado para invocar Count reflexivamente:

// get Enumerable (which holds the extension methods) 
Type enumerableT = typeof(Enumerable); 

// get the Count-method (there are only two, you can check the parameter-count as in above 
// to be certain. Here we know it's the first, so I use the first: 
MemberInfo member = enumerableT.GetMember("Count")[0]; 

// create the generic method (instead of int, replace with typeof(yourtype) in your code) 
MethodInfo method = ((MethodInfo) member).MakeGenericMethod(typeof(int)); 

// invoke now becomes trivial 
int count = (int)method.Invoke(null, new object[] { yourcollection }); 

los trabajos anteriores, debido a que no es necesario utilizar el tipo genérico de IEnumerable<> para poder invocar Count, que es una extensión de Enumerable y toma un argumento de IEnumerable<T> como primer param (es una extensión), pero no es necesario que especifique eso.

Tenga en cuenta que, a partir de la lectura de su pregunta, me parece que debería usar genéricos para sus tipos, lo que agrega seguridad de tipos en su proyecto y todavía le permite usar Count o lo que sea. Después de todo, lo único cierto es que todos son Enumerable, ¿verdad? Si es así, ¿quién necesita reflexión?

+0

El motivo de la reflexión es simple: lo que etiqueta como "yourtype" no se conoce en tiempo de compilación (o al menos cambia con frecuencia). – mbi

+0

@embee: eso, lo entendí. Pero si cambia con frecuencia (se genera), es ideal para genéricos. Pero honestamente, no conozco lo suficiente de su situación para saber con certeza si su enfoque es bueno o no. – Abel

+0

Probablemente no entiendo los genéricos lo suficiente como para seguir su sugerencia, lo siento. En este escenario, MyCollections es algo que debes dar por hecho. – mbi

Cuestiones relacionadas