Feedback

Verschachtelte Klasseninstanzen rekursiv durchsuchen

Sprache: C#

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: [code] //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); } } [/code] 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 [b].NET-Framework Version 4.5[/b]
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;
      }
    }
  }
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;
      }
    }
  }

2 Kommentare

  1. Das Problem per Reflection zu lösen ist recht langsam. Linq ähnlich könnte man es so aufbauen:

    [code]IEnumerable IterateChain(T obj, Func what, Func 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);
    }[/code]

    Edit: Im Code sind ein paar spitze Klammern drin, leider werden die hier in dem Kommentar nicht angezeigt: http://pastebin.com/sj2VN7tL