Рекурсивная итерация по свойствам объекта создает исключение StackOverflowException - PullRequest
0 голосов
/ 02 сентября 2018

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

void GetProps(object obj)
{
    if (obj == null)
        return;

    var objType = obj.GetType();
    var properties = objType.GetProperties();

    foreach (var property in properties)
    {
        object value = property.GetValue(obj, null);

        if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
        {
            var enumerable = (IEnumerable)value;

            foreach (object child in enumerable)
                GetProps(child);
        }
        else
        {
            GetProps(value);
        }
    }
}

Объект очень сложный (более 30 классов). Я получаю StackOverflowException, когда углубляюсь в объект на GetProps(value.

Есть ли способ перехватить исключение и проверить, почему оно не работает, и решить проблему?

EDIT

Я добавил отказоустойчивый в верхней части метода:

if (visited.Contains(obj))
    return;

visited.Add(obj);

Проблема не в циклических ссылках (которых у меня нет), а в типах свойств, таких как DateTime, int и decimal. который предполагал, что они примитивны, но свойство IsPrimitive равно false.

Могу ли я различать такие типы от моих собственных классов?

Ответы [ 2 ]

0 голосов
/ 02 сентября 2018

Вот пример, не использующий рекурсию, который использует подход явного стека Эрика Липперта . Я не знаю, соответствует ли поведение со строками тому, что вы хотели, но это может помешать вашему стеку взорваться:

public static IEnumerable<object> GetPropertiesDepthFirst(object obj)
{
    if (obj == null)
        yield break;

    var stack = new Stack<object>();
    stack.Push(obj);

    while (stack.Count > 0)
    {
        var current = stack.Pop();
        yield return current;

        var objType = current.GetType();
        var properties = objType.GetProperties();

        foreach (var property in properties)
        {
            object value = property.GetValue(current, null);
            if (value == null)
                continue;

            if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
            {
                var enumerable = (IEnumerable)value;
                foreach (object child in enumerable)
                    stack.Push(child);
            }
            else
            {
                yield return value;
            }
        }
    }
}

Также для IEnumerables с определенными индексами вы можете исключить индексированные свойства:

objType.GetProperties().Where(p => p.GetIndexParameters().Length == 0)
0 голосов
/ 02 сентября 2018

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

Скорее всего, это происходит потому, что у вас есть круговая ссылка. То есть объект, который содержит другой объект, который содержит ссылку на исходный объект. Между двумя классами может быть произвольное количество уровней иерархии.

Вам нужно реализовать какой-то механизм, чтобы прекратить обход объекта, который вы уже прошли, например, с помощью хеш-набора.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...