Feedback

C# - Verschachtelte Klasseninstanzen rekursiv durchsuchen

Veröffentlicht von am 11/21/2015
(0 Bewertungen)
Manchmal gibt es die Problematik, dass Klassen Properties vom Typ der Klasse selbst halten. Das heißt letztendlich natürlich auch, dass Instanzen dieser Klasse "in sich selbst verschachtelt" werden können.
Beispiel:

  //Klasse zum Halten der Daten
public class Test
{
public Test()
{

}

public Test InnerTest { get; set; }
public string Text { get; set; }
public int Count { get; set; }
}

class Program
{
public static void Main()
{
//Verschachtelung erzeugen
Test foobar = new Test()
{
Text = "Level1",
InnerTest = new Test()
{
Text = "Level2",
Count = 33,
InnerTest = new Test()
{
InnerTest = new Test()
{
Text = "Level3",
Count = 44
}
}
}
};

//Instanzen des Properties "Text" mit dem Wert "Level3" suchen
IEnumerable<Test> level3Result = foobar.DeepWhere(p => p.Text == "Level3");
//Instanzen des Properties "Count" mit dem Wert "33" suchen
IEnumerable<Test> countResult = foobar.DeepWhere(p => p.Count == 33);
}
}

Dieses Snippet dient dazu, diese "Verschachtelung" aufzulösen und die verschiedenen Instanzen durchsuchen zu können.
Die eigentliche Funktionalität stellt die Extension-Function "DeepWhere" bereit. Diese Methode liefert ein IEnumerable der Instanzen zurück, die den angegebenen Suchkritieren entsprechen.

Das entsprechende Snippet erfordert die .NET-Framework Version 4.5
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

  public static class Extension
  {
    /// <summary>
    /// Filters a sequence of values recursive based on a predicate. 
    /// </summary>
    /// <param name="predicate">A function to test each element for a condition.</param>
    /// <returns>An IEnumerable that contains elements from the input sequence that satisfy the condition.</returns>
    public static IEnumerable<TSource> DeepWhere<TSource>(this TSource instance, Func<TSource, bool> predicate)
    {
      if (instance != null)
      {
        if (predicate(instance))
          yield return instance;

        foreach (TSource source in instance
          .GetType().GetProperties()
          .Where(p => p.PropertyType.Equals(typeof(TSource)))
          .SelectMany(p => ((TSource)p.GetValue(instance)).DeepWhere(predicate)))
          yield return source;
      }
    }
  }
Abgelegt unter c#.

2 Kommentare zum Snippet

Anonymous2 schrieb am 11/22/2015:
Das Problem per Reflection zu lösen ist recht langsam. Linq ähnlich könnte man es so aufbauen:

IEnumerable<T> IterateChain<T>(T obj, Func<T, T> what, Func<T, bool> when) where T : class
{
if (obj == null)
{
throw new ArgumentNullException(nameof(obj));
}

T current = obj;
while (true)
{
current = what(current);
if (current == null)
{
break;
}
if (when == null || when(current))
{
yield return current;
}
else
{
break;
}
}
}

//das entspricht deinem foobar.Search("Count", 33)
foreach (var t in IterateChain(foobar, t => t.InnerTest, t => t.Count == 33))
{
Console.WriteLine(t.Text);
}


Edit: Im Code sind ein paar spitze Klammern drin, leider werden die hier in dem Kommentar nicht angezeigt: http://pastebin.com/sj2VN7tL
floriankolb schrieb am 11/22/2015:
Danke für die Anregung! Eine Func ist dafür perfekt geeignet. Ich habe meinen Code ein bisschen angepasst
 

Logge dich ein, um hier zu kommentieren!