Подтвердить что ты не робот

Доступ к коллекции через отражение

Есть ли способ для итерации (предпочтительно через foreach) над коллекцией с использованием отражения? Я повторяю свойства объекта с использованием отражения, и когда программа попадает к типу, который представляет собой коллекцию, я бы хотел, чтобы он перебирал содержимое коллекции и имел возможность доступа к объектам в коллекции.

В настоящий момент у меня есть атрибут, установленный для всех моих свойств, с флагом IsCollection, установленным в true для свойств, которые являются коллекциями. Мой код проверяет этот флаг, и если он верен, он получает отражение типа с использованием. Есть ли способ вызвать GetEnumerator или Items каким-либо образом в коллекции, чтобы иметь возможность перебирать элементы?

4b9b3361

Ответ 1

У меня была эта проблема, но вместо того, чтобы использовать отражение, я просто проверял, был ли он IEnumerable. Все коллекции реализуют это.

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

    }
} else {
   // reflect over item
}

Ответ 2

Я попытался использовать подобную технику, как предложил Даррен, однако просто будьте осторожны, что не только коллекции реализуют IEnumerable. string например, также IEnumerable и будет перебирать символы.

Здесь небольшая функция, которую я использую, чтобы определить, является ли объект коллекцией (который будет перечислим, так как ICollection также IEnumerable).

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

Ответ 3

Просто получите значение свойства, а затем введите его в IEnumerable. Вот некоторый (непроверенный) код, чтобы дать вам идею:

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

Лучшее, что вы, вероятно, могли бы сделать, это проверить, реализует ли объект определенные интерфейсы коллекции - возможно, IEnumerable будет всем, что вам нужно. Тогда это просто вопрос вызова GetEnumerator() с объекта и использование IEnumerator.MoveNext() и IEnumerator.Current для работы с вашей коллекцией.

Это не поможет вам, если коллекция не реализует эти интерфейсы, но если это не так, большая часть коллекции, я полагаю.

Ответ 5

Просто для информации может быть кто-то поможет... У меня был класс с вложенными классами и набором некоторых других классов. Я хотел сохранить значения свойств класса, а также вложенные классы и набор классов. Мой код выглядит следующим образом:

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

Вызывается эта функция

LogObject(obj, 0);

Однако у меня есть некоторые структуры внутри моих классов, и мне нужно выяснить, как получить их значения. Мооэевр, у меня есть один Лист. Мне также нужно получить их значение... Я отправлю сообщение, если обновляю свой код.

Ответ 6

Когда вы используете отражение, вы не обязательно используете экземпляр этого объекта. Вам нужно будет создать экземпляр этого типа, который сможет перебирать свойства объекта. Поэтому, если вы используете отражение, используйте метод ConstructorInfo.Invoke() (?), Чтобы создать новый экземпляр или указать экземпляр типа.

Ответ 7

Довольно простой подход заключался бы в том, чтобы вводить объект в качестве коллекции и непосредственно использовать это.

Ответ 8

Я бы посмотрел на метод Type.FindInterfaces. Это может отфильтровывать интерфейсы, реализованные данным типом. Как и в PropertyInfo.PropertyType.FindInterfaces(filterMethod, filterObjects). Вы можете фильтровать IEnumerable и посмотреть, будут ли возвращены какие-либо результаты. MSDN имеет прекрасный пример в документации по методу.

Ответ 9

Если вы не используете экземпляр объекта, а скорее тип, вы можете использовать следующее:

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