代码之家  ›  专栏  ›  技术社区  ›  Ivan-Mark Debono

递归遍历对象的属性会引发StackOverflowException

  •  1
  • Ivan-Mark Debono  · 技术社区  · 6 年前

    我使用以下方法递归遍历对象的属性:

    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 .

    编辑

    我在方法的顶部添加了一个故障保护:

    if (visited.Contains(obj))
        return;
    
    visited.Add(obj);
    

    DateTime , int decimal . 假设它们是原始的但是 IsPrimitive 财产是 false .

    我能区分这些类型和我自己的类吗?

    2 回复  |  直到 6 年前
        1
  •  0
  •   Parrish Husband    6 年前

    下面是一个不使用递归的示例,它利用了Eric Lippert的 explicit stack approach

    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)
    
        2
  •  4
  •   Daniel Hilgarth Richard    6 年前

    A StackOverflowException can't be caught

    很可能发生这种情况是因为您有一个循环引用。即,包含另一个对象的对象,该对象包含对原始对象的引用。两个类之间可能有任意数量的层次结构级别。