web-dev-qa-db-fra.com

Accéder à une collection par réflexion

Existe-t-il un moyen d’itérer (par foreach de préférence) une collection en utilisant la réflexion? Je parcourt les propriétés d'un objet à l'aide de la réflexion et, lorsque le programme entre dans un type qui est une collection, j'aimerais qu'il parcoure le contenu de la collection et puisse accéder aux objets de la collection.

À l'heure actuelle, un attribut est défini sur toutes mes propriétés, avec un indicateur IsCollection défini sur true sur les propriétés qui sont des collections. Mon code vérifie cet indicateur et, s'il est vrai, il obtient le type en utilisant la réflexion. Existe-t-il un moyen d'invoquer GetEnumerator ou Items d'une manière ou d'une autre sur une collection pour pouvoir effectuer une itération sur les éléments?

29
Bob Wintemberg

J'avais ce problème, mais au lieu d'utiliser la réflexion, je me suis contenté de vérifier si c'était IEnumerable. Toutes les collections implémentent cela.

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

    }
} else {
   // reflect over item
}
35
Darren Kopp

J'ai essayé d'utiliser une technique similaire à celle suggérée par Darren, mais sachez que non seulement les collections implémentent IEnumerable. string par exemple est également IEnumerable et va parcourir les caractères.

Voici une petite fonction que j'utilise pour déterminer si un objet est une collection (qui sera énumérable également puisque ICollection est également IEnumerable).

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

Obtenez simplement la valeur de la propriété, puis convertissez-la en un IEnumerable. Voici du code (non testé) pour vous donner une idée:

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
8
jop

Juste pour information, ce sera peut-être utile à quelqu'un… J'ai eu une classe avec des classes imbriquées et une collection de quelques autres classes. Je voulais enregistrer les valeurs de propriété de la classe, ainsi que les classes imbriquées et la collection de classes. Mon code est le suivant:

 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);
                    }
                }
            }
        }
    }

Un appelé cette fonction 

LogObject(obj, 0);

Cependant, j'ai quelques structures dans mes classes et je dois trouver comment obtenir leurs valeurs. Moreoevr, j'ai de la peine. J'ai besoin d'obtenir leur valeur aussi .... Je posterai si je mets à jour mon code.

2
shazia

Le mieux que vous puissiez faire serait probablement de vérifier si l’objet implémente certaines interfaces de collection - IEnumerable serait probablement tout ce dont vous avez besoin. Ensuite, il suffit d'appeler GetEnumerator () hors de l'objet et d'utiliser IEnumerator.MoveNext () et IEnumerator.Current pour parcourir la collection.

Cela ne vous aidera pas si la collection n'implémente pas ces interfaces, mais si c'est le cas, ce n'est pas vraiment une collection, je suppose.

2
Andy

Je regarderais la méthode Type.FindInterfaces. Cela peut filtrer les interfaces implémentées par un type donné. Comme dans PropertyInfo.PropertyType.FindInterfaces (filterMethod, filterObjects). Vous pouvez filtrer par IEnumerable et voir si des résultats sont renvoyés. MSDN a un excellent exemple dans la documentation de la méthode.

0
Eric Swann

Si vous n'utilisez pas une instance de l'objet mais plutôt un type, vous pouvez utiliser les éléments suivants:

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

Lorsque vous utilisez la réflexion, vous n'utilisez pas nécessairement une instance de cet objet. Vous devez créer une instance de ce type pour pouvoir parcourir les propriétés de l'objet. Ainsi, si vous utilisez la réflexion, utilisez la méthode ConstructorInfo.Invoke () (?) Pour créer une nouvelle instance ou pointez sur une instance du type.

0
Adam Driscoll

Une approche plutôt simple consisterait à transtyper l'objet en tant que collection et à l'utiliser directement.

0
nullDev