2008-09-19 17 views
28

¿Hay alguna manera de iterar (preferiblemente a través de foreach) sobre una colección utilizando la reflexión? Estoy iterando sobre las propiedades de un objeto utilizando la reflexión, y cuando el programa llega a un tipo que es una colección, me gustaría iterar sobre los contenidos de la colección y poder acceder a los objetos en la colección.Accediendo a una colección mediante reflexión

Por el momento tengo un atributo establecido en todas mis propiedades, con un indicador IsCollection establecido en verdadero en las propiedades que son colecciones. Mi código busca esta bandera y si es verdadera, obtiene el tipo usando reflexión. ¿Hay alguna manera de invocar GetEnumerator o Items de alguna manera en una colección para poder iterar sobre los elementos?

Respuesta

33

Tuve este problema, pero en lugar de usar el reflejo, terminé simplemente comprobando si era IEnumerable. Todas las colecciones implementan eso.

if (item is IEnumerable) 
{ 
    foreach (object o in (item as IEnumerable)) 
    { 

    } 
} else { 
    // reflect over item 
} 
+2

como no es necesario, lo verificó con el es así que un simple elenco está bien – ShuggyCoUk

+1

fundición con como es más rápido que el elemento (IEnumerable). –

+4

Podría ser más eficiente hacer: var enumItem = item como IEnumerable; if (enumItem! = Null) // hacer foreach –

0

Cuando usa la reflexión, no necesariamente utiliza una instancia de ese objeto. Debería crear una instancia de ese tipo para poder iterar a través de las propiedades del objeto. Entonces, si usa la reflexión, use el método ConstructorInfo.Invoke() (?) Para crear una nueva instancia o punto a una instancia del tipo.

2

Lo mejor que probablemente podría hacer es comprobar si el objeto implementa ciertas interfaces de recopilación, probablemente IEnumerable sería todo lo que necesita. Entonces solo se trata de llamar a GetEnumerator() fuera del objeto y usar IEnumerator.MoveNext() e IEnumerator.Current para abrirse paso en la colección.

Esto no lo ayudará si la colección no implementa esas interfaces, pero si ese es el caso, no es realmente una gran colección, supongo.

0

Un enfoque bastante directo sería escribir el objeto como colección y usarlo directamente.

8

Simplemente obtenga el valor de la propiedad y luego vuélvala a un IEnumerable. Aquí hay alguna (no probado) código para darle una idea:

ClassWithListProperty obj = new ClassWithListProperty(); 
obj.List.Add(1); 
obj.List.Add(2); 
obj.List.Add(3); 

Type type = obj.GetType(); 
PropertyInfo listProperty = type.GetProperty("List", BindingFlags.Public); 
IEnumerable listObject = (IEnumerable) listProperty.GetValue(obj, null); 

foreach (int i in listObject) 
    Console.Write(i); // should print out 123 
29

He tratado de utilizar una técnica similar como se sugiere Darren, sin embargo, sólo ten en cuenta que no sólo colecciones implementan IEnumerable. string por ejemplo también es IEnumerable y repetirá sobre los caracteres.

Aquí hay una pequeña función que estoy usando para determinar si un objeto es una colección (que también será enumerable dado que ICollection también es IEnumerable).

public bool isCollection(object o) 
    { 
     return typeof(ICollection).IsAssignableFrom(o.GetType()) 
      || typeof(ICollection<>).IsAssignableFrom(o.GetType()); 
    } 
+0

¡Genial, gracias, eso solucionó mi problema! – endian

0

Me gustaría ver el método Type.FindInterfaces. Esto puede filtrar las interfaces implementadas por un tipo determinado. Como en PropertyInfo.PropertyType.FindInterfaces (filterMethod, filterObjects). Puede filtrar por IEnumerable y ver si se devuelve algún resultado. MSDN tiene un gran ejemplo en la documentación del método.

0

Si usted no está utilizando una instancia del objeto, sino más bien un tipo, puede utilizar el siguiente:

// type is IEnumerable 
if (type.GetInterface("IEnumerable") != null) 
{ 
} 
2

Una información puede ser que será de ayuda de alguien ... que tenía una clase con clases anidadas y colección de algunas otras clases. Quería guardar los valores de propiedad de la clase, así como las clases anidadas y la colección de clases.Mi código es el siguiente:

public void LogObject(object obj, int indent) 
    { 
     if (obj == null) return; 
     string indentString = new string(' ', indent); 
     Type objType = obj.GetType(); 
     PropertyInfo[] properties = objType.GetProperties(); 

     foreach (PropertyInfo property in properties) 
     { 
      Type tColl = typeof(ICollection<>); 
      Type t = property.PropertyType; 
      string name = property.Name; 


      object propValue = property.GetValue(obj, null); 
      //check for nested classes as properties 
      if (property.PropertyType.Assembly == objType.Assembly) 
      { 
       string _result = string.Format("{0}{1}:", indentString, property.Name); 
       log.Info(_result); 
       LogObject(propValue, indent + 2); 
      } 
      else 
      { 
       string _result = string.Format("{0}{1}: {2}", indentString, property.Name, propValue); 
       log.Info(_result); 
      } 

      //check for collection 
      if (t.IsGenericType && tColl.IsAssignableFrom(t.GetGenericTypeDefinition()) || 
       t.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == tColl)) 
      { 
       //var get = property.GetGetMethod(); 
       IEnumerable listObject = (IEnumerable)property.GetValue(obj, null); 
       if (listObject != null) 
       { 
        foreach (object o in listObject) 
        { 
         LogObject(o, indent + 2); 
        } 
       } 
      } 
     } 
    } 

Una llama esta función

LogObject(obj, 0); 

Sin embargo, tengo algunas estructuras dentro de mis clases y tengo que encontrar la manera de obtener sus valores. Moreoevr, tengo algo de LIst. Necesito obtener su valor también ... Publicaré si actualizo mi código.

Cuestiones relacionadas