使用Reflection查找复杂对象的所有字符串值

时间:2013-07-08 07:06:10

标签: c# reflection

我遇到一些问题,找到一种在对象上使用Reflection查找所有字符串值的简单方法。

我需要递归扫描一个对象,包括任何列表,一个匹配值的字符串数组。 到目前为止,我正在尝试返回所有值的列表,然后我可以检查该列表,但是我遇到了一个问题,如果对象包含任何循环依赖项,这会抛出StackOverflowException

到目前为止我所拥有的:(我知道,我知道,但这是一项正在进行的工作:))

private IEnumerable<string> FindStringValues(object obj)
{
    if (obj != null)
    {
        Type type = obj.GetType();
        if (type == typeof(string))
        {
            yield return (string)obj;
        }
        else if (type.IsArray)
        {
            var array = obj as Array;
            foreach (var item in array)
            {
                foreach (var str in FindStringValues(item))
                {
                    yield return str;
                }
            }
        }
        else if (type.IsClass)
        {
            FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
            foreach (FieldInfo field in fields)
            {
                object fieldValue = field.GetValue(obj);
                if (fieldValue == null)
                    continue;

                foreach (var str in FindStringValues(fieldValue))
                {
                    yield return str;
                }
            }
        }
    }
}

所以我正试图找到一种方法让这个StackOverflowException安全,如果有人可以帮我解决如何找到bool如果找到匹配的nsinreal则更好提供的价值。


编辑:

感谢public static bool FindStringValue(object obj, string valueToFind) { return FindStringValue(obj, valueToFind, new List<object>()); } private static bool FindStringValue(object obj, string valueToFind, IList<object> visitedObjects) { if (obj == null && !visitedObjects.Any(item => Object.ReferenceEquals(item, obj))) { if (!(obj is string)) { visitedObjects.Add(obj); } Type type = obj.GetType(); if (type == typeof(string)) { return (obj).ToString() == valueToFind; } if (typeof(IEnumerable).IsAssignableFrom(type)) { var array = obj as IEnumerable; foreach (var item in array) { if (FindStringValue(item, valueToFind, visitedObjects)) { return true; } } return false; } if (type.IsClass) { FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); foreach (FieldInfo field in fields) { object item = field.GetValue(obj); if (item == null) continue; if (FindStringValue(item, valueToFind, visitedObjects)) { return true; } } } } return false; } 的回答,我能够完成目标:)以下添加,对其他人有帮助

{{1}}

1 个答案:

答案 0 :(得分:4)

  1. 任何列表,任何数组 - &gt;任何IEnumerable。所以你可以替换:

    if (type.IsArray)
    

    要:

    if (typeof(IEnumerable).IsAssignableFrom(type))
    
  2. 要防止StackOverflowException,请不要访问被访问对象。你喜欢这个主意吗?因此,首先我们必须选择:我们不希望访问所有访问过的对象或只访问父对象。在这种情况下,我们不会访问所有访问过的对象。所以,modifcate标题:

    // visitedObjects can changes
    private static IEnumerable<string> _FindStringValues(object obj, IList<object> visitedObjects)
    

    现在,在此方法中修改FindStringValues调用_FindStringValues的所有调用:

    _FindStringValues(item, visitedObjects)
    

    并添加可用的包装方法:

    private static IEnumerable<string> FindStringValues(object obj) {
        return _FindStringValues(obj, new List<object>());
    }
    

    _FindStringValues中,您必须将传递给此方法的每个obj添加到访问对象列表中。

    visitedObjects.Add(obj);
    

    但在此之前,您必须检查此对象是否未被访问过。您必须使用Object.ReferenceEquals

    if (visitedObjects.Any(item => Object.ReferenceEquals(item, obj)))
        yield break;
    

    要小心,如果要查看来自不同对象的相等字符串,则必须防止向访问对象列表添加字符串。为什么? String interning。像这样:

    if (!(obj is string))
        visitedObjects.Add(obj);
    
  3. 经过一些代码格式化后,我得到了这个:code