views:

4006

answers:

9

Is there a way to iterate (through foreach preferably) over a collection using reflection? I'm iterating over the properties in an object using reflection, and when the program gets to a type that is a collection, I'd like it to iterate over the contents of the collection and be able to access the objects in the collection.

At the moment I have an attribute set on all of my properties, with an IsCollection flag set to true on the properties that are collections. My code checks for this flag and if it's true, it gets the Type using reflection. Is there a way to invoke GetEnumerator or Items somehow on a collection to be able to iterate over the items?

A: 

When your using reflection you aren't necessarily using an instance of that object. You would have to create an instance of that type of be able to iterate through the object's properties. So if you are using reflection use the ConstructorInfo.Invoke() (?) method to create a new instance or point to an instance of the type.

Adam Driscoll
+1  A: 

The best you could probably do would be to check if the object implements certain collection interfaces - probably IEnumerable would be all that you need. Then it's just a matter of calling GetEnumerator() off of the object, and using IEnumerator.MoveNext() and IEnumerator.Current to work your way through the collection.

This won't help you if the collection doesn't implement those interfaces, but if that's the case it's not really much of a collection, I suppose.

Andy
+8  A: 

I had this issue, but instead of using reflection, i ended up just checking if it was IEnumerable. All collections implement that.

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

    }
} else {
   // reflect over item
}
Darren Kopp
the as in not needed, you checked it with the is so a simple cast is fine
ShuggyCoUk
casting with as is faster than (IEnumerable)item.
Darren Kopp
A: 

Can you provide a code snippet to explain your question a bit?

A rather straight forward aproach would be to type cast the object as the collection and directly use that.

nullDev
+3  A: 

Just get the value of the property and then cast it into an IEnumerable. Here is some (untested) code to give you an 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
jop
+3  A: 

I've tried to use a similar technique as Darren suggested, however just beware that not just collections implement IEnumerable. string for instance is also IEnumerable and will iterate over the characters.

Here's a small function I'm using to determine if an object is a collection (which will be enumerable as well since ICollection is also IEnumerable).

    public bool isCollection(object o)
    {
        return typeof(ICollection).IsAssignableFrom(o.GetType())
            || typeof(ICollection<>).IsAssignableFrom(o.GetType());
    }
Nagyman
Great, thank you, that fixed my issue!
endian
A: 

I would look at the Type.FindInterfaces method. This can filter out the interfaces implemented by a given type. As in PropertyInfo.PropertyType.FindInterfaces(filterMethod, filterObjects). You can filter by IEnumerable and see if any results are returned. MSDN has a great example in the method documentation.

A: 

pi = o.GetType().GetProperty(propname); MethodInfo [] mi = pi.GetAccessors(); try { ICollection cb = (ICollection)pi.GetGetMethod().Invoke(o, null); object[] ocb = new object [cb.Count]; cb.CopyTo(ocb, 0); // the collection is in the array .. // so you may loop through a for Loop ..

}

// ENJOY.

Ghassan El-Nemr
A: 

If you're not using an instance of the object but rather a Type, you can use the following:

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