I have a need that is a bit similar to this question, except that it requires a deeper exploration of the source object.
Here is a code sample:
public class Target {};
public class Analyzed
{
public Target EasyOne { get; set; }
public IList<Target> ABitMoreTricky { get; set; }
public IList<Tuple<string, Target>> Nightmare { get; set; }
}
From an instance of Analyzed
, I want to extract all the Target
instances.
In order to ease the exploration, we can assume the following:
For now, EasyOne
is... easy, but I am looking for some strategy to get all the Target
instances lost in more tricky structures.
How about something along these lines:
public List<T> FindAllInstances<T>(object value) where T : class
{
HashSet<object> exploredObjects = new HashSet<object>();
List<T> found = new List<T>();
FindAllInstances(value, exploredObjects, found);
return found;
}
private void FindAllInstances<T>(object value, HashSet<object> exploredObjects, List<T> found) where T : class
{
if (value == null)
return;
if (exploredObjects.Contains(value))
return;
exploredObjects.Add(value);
IEnumerable enumerable = value as IEnumerable;
if (enumerable != null)
{
foreach(object item in enumerable)
{
FindAllInstances<T>(item, exploredObjects, found);
}
}
else
{
T possibleMatch = value as T;
if (possibleMatch != null)
{
found.Add(possibleMatch);
}
Type type = value.GetType();
PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetProperty);
foreach(PropertyInfo property in properties)
{
object propertyValue = property.GetValue(value, null);
FindAllInstances<T>(propertyValue, exploredObjects, found);
}
}
private void TestIt()
{
Analyzed analyzed = new Analyzed()
{
EasyOne = new Target(),
ABitMoreTricky = new List<Target>() { new Target() },
Nightmare = new List<Tuple<string, Target>>() { new Tuple<string, Target>("", new Target()) }
};
List<Target> found = FindAllInstances<Target>(analyzed);
MessageBox.Show(found.Count.ToString());
}
You could go the reflection way, and have special treatment for all the containers you know (IEnumerable, IDictionary, all Tuples, and who knows what else), or you can actually implement what @Adrian Iftode jokingly said in a comment.
I don't think you really want to serialize to XML and then parse it. It will work, but it will require all your objects to be XML serializable, which, if I am not mistaken, requires all serialized data to be public.
You should use the ordinary serialization, but define your own custom formatter that does nothing but track the objects you're looking for. Here's an example of a simple custom formatter.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With