I am having a few issues finding a simple way to find all string values using Reflection
on an object.
I need to scan an object recursively including any lists, arrays for a string that matches a value.
So far I am trying to return a list of all the values then I can check that list, however I have run into a problem where this can throw a StackOverflowException
if the object contains any circular dependencies.
What I have so far: (I know, I know, but its a work in progress :))
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;
}
}
}
}
}
So I'm trying to find a way to make this StackOverflowException
safe, even better if anyone can help me figure out how to make this a bool
to return if a match is found with a supplied value.
EDIT:
Thanks to the answer from nsinreal
I was able to accomplish the goal :) added below incase its helpful to others
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;
}