Accessing a Collection Through Reflection

2020-02-08 05:24发布

问题:

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?

回答1:

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
}


回答2:

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


回答3:

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


回答4:

Just for information may be it will be of someone's help... I had a class with nested classes and collection of some other classes. I wanted to save the property values of the class as well nested classes and collection of classes. My code is as follows:

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

An called this function

LogObject(obj, 0);

However, I have some structs inside my classes and I need to figure out how to get their values. Moreoevr, I have some LIst. I need to get their value as well.... I will post if I update my code.



回答5:

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.



回答6:

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.



回答7:

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.



回答8:

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


回答9:

A rather straightforward approach would be to type cast the object as the collection and directly use that.