How to iterate a C # object, looking for all instances of a certain type to create a separate list of these instances?

I have a need that is a bit like this question , except that it requires a more in-depth study of the source object.

Here is a sample code:

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 the instance of Analyzed I want to extract all instances of Target .

To facilitate the study, we can assume the following:

  • Explore only properties.
  • There is no infinite reference cycle.

For now, EasyOne ... is simple, but I'm looking for some strategy so that all Target instances get lost in more complex structures.

+4
source share
4 answers

How about something in this direction:

  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()); } 
+10
source

You can follow the path of reflection and have special treatment for all containers that you know (IEnumerable, IDictionary, all Tuples, and who knows what else), or you can really implement what @Adrian Iftode jokingly said in a comment.

I don't think you really want to serialize XML and then parse it. It will work, but will require that all of your objects be XML-serializable, which, if I am not mistaken, requires that all serialized data be publicly available.

You should use regular serialization, but define your own formatter that does nothing but track the objects you are looking for. Here is an example of simple custom formatting.

+1
source

You can do this using reflection. Solve two tasks:

  • Get all type properties. Type.GetProperties() will do this.

  • Determine if the property type is of type Target or is a generic type with Target as an argument to the type. You can use Type.IsGenericType to check if the type is generic, and Type.GetGenericArguments to get the actual type arguments. If a match is found, you must repeat this generic type starting with 1 and perform the match described in 2.

Thus, using reflection and recursion on generic types, you should do what you want.

0
source

Dim tTargets () = {New list (s) of {New Target ("Joe"), New Target ("Bob")}, "New target" ("Veronica"), "New tuple" ("String", " Target ", DateTime, Target) (" Tuple ", New target (" Henry "), DateTime.Now, New Target (" Deacon "))}

 Sub ShowMeTheTargets(ByVal tRoot As Object, ByVal tLevel As Int32) Dim tCount As Int64 = 0 Dim tCountName As String = "Length" If Nothing Is tRoot Then Exit Sub End If If tRoot.GetType Is GetType(Target) Then RTB.AppendText("Found: " & CType(tRoot, Target).Name & vbCrLf) ' ' Assume Target is not a Target container. ' Exit Sub End If If LEVEL_MAX = tLevel Then ' ' We don't want to scan this object graph any deeper ' Exit Sub End If If (Nothing Is tRoot.GetType.GetInterface("IEnumerable")) Then For Each tProperty As PropertyInfo In tRoot.GetType.GetProperties ShowMeTheTargets(tProperty.GetValue(tRoot, Nothing), tLevel + 1) Next Else For Each tObject As Object In tRoot ShowMeTheTargets(tObject, tLevel + 1) Next RTB.AppendText(tCount & vbCrLf) End If End Sub 
0
source

Source: https://habr.com/ru/post/1399204/


All Articles