c# - example - Accediendo a una colección a través de la reflexión
remarks c# (9)
¿Hay alguna manera de iterar (preferiblemente a través de foreach) sobre una colección usando 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.
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?
Cuando utilizas la reflexión, no necesariamente estás usando 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.
Intenté utilizar una técnica similar a la sugerida por Darren, sin embargo, tenga en cuenta que no solo las 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());
}
Lo mejor que probablemente podría hacer sería verificar 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.
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.
Si no está utilizando una instancia del objeto sino un Tipo, puede usar lo siguiente:
// type is IEnumerable
if (type.GetInterface("IEnumerable") != null)
{
}
Simplemente obtenga el valor de la propiedad y luego vuélvala a un IEnumerable. Aquí hay un código (no probado) para que tenga 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
Solo para información puede ser de la ayuda de alguien ... Tuve una clase con clases anidadas y una 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 llamada esta función
LogObject(obj, 0);
Sin embargo, tengo algunas estructuras dentro de mis clases y necesito descubrir cómo obtener sus valores. Moreoevr, tengo algo de LIst. Necesito obtener su valor también ... Publicaré si actualizo mi código.
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
}
Un enfoque bastante directo sería escribir el objeto como colección y usarlo directamente.