Как получить все подобъекты из CustomObject с n дочерними / подчиненными и т. Д. - PullRequest
4 голосов
/ 12 декабря 2011

У меня есть CustomObject с n детьми.Эти дети представляют собой список пользовательских объектов.Примерно так:

public class CustomObject
{
    public List<CustomObject> Children = new List<CustomObject>();
}

То, что я ищу, - это наиболее эффективный способ получения ВСЕХ детей, их детей, дочерних элементов и т. Д. Из одного экземпляра CustomObject.Есть ли лучший способ, чем циклически проходить все строки, пока я не достигну конца (ноль)?

(C #, .NET 3.5)

Чтобы сделать это более понятным, я приведу пример структуры:

//root object
CustomObject.Children ->
    CustomObject.Children ->
         CustomObject
         CustomObject
    CustomObject.Children ->
         CustomObject.Children ->
             CustomObject
         CustomObject
    CustomObject

В этом случае мне нужно получить ВСЕ пользовательские объекты ниже корневого объекта.

Ответы [ 6 ]

7 голосов
/ 12 декабря 2011

Нет, вам просто нужно пройтись по всему. Вы можете использовать рекурсивный метод:

public void GetItems(List<CustomObject> list) {
  list.Add(this);
  foreach (CustomObject child in Childs) {
    child.GetItems(list);
  }
}

Использование:

List<CustomObject> items = new List<CustomObject>();
someCustomObject.GetItems(items);

Примечание: форма множественного числа child - children .

4 голосов
/ 12 декабря 2011

Я использую этот метод Расширения:

public static IEnumerable<T> Flatten<T>(this IEnumerable<T> hierarchy, Func<T, IEnumerable<T>> lambda)
    {
        var result = new List<T>();

        foreach (var item in hierarchy)
        {
            result.AddRange(Flatten(lambda(item), lambda));
            if (!result.Contains(item))
                result.Add(item);
        }

        return result;
    }

Вы будете называть его так:

MyObject.Childs.Flatten(c => c.Childs);

вот довольно уродливое переопределение, которое добавляет корневой объект, если требуется:

public static IEnumerable<T> Flatten<T>(this T @this, Func<T, IEnumerable<T>> lambda)
{
    return new[] { @this }.Flatten(lambda);
}
2 голосов
/ 12 декабря 2011

Вам придется перебирать каждую коллекцию Childs. Вы можете рассмотреть что-то вроде этого, однако:

public class CustomObject
{
    public List<CustomObject> Childs = new List<CustomObject>();

    protected IEnumerable<CustomObject> GetDecendants()
    {
        foreach (var child in Childs)
        {
            yield return child;
            foreach (var grandchild in child.GetDecendants())
            {
                yield return grandchild;
            }
        }
    }
}
2 голосов
/ 12 декабря 2011

Наверное, нет, но это полностью зависит от того, как вы это сделали. Вы можете использовать yield рекурсивно:

public IEnumerable<CustomObject> AndChildren()
{
  yield return this;
  foreach(var child in Childs)
  {
    foreach(var nested in child.AndChildren())
    {
      yield return nested;
    }
  }
}

Что имеет очевидное преимущество, заключающееся в позднем связывании и, следовательно, намного лучше в использовании вашей памяти. Недостатком является то, что он чрезвычайно хрупок с точки зрения любых изменений, вносимых в любой узел в дереве (итераторы foreach будут вызывать исключение для следующего, если какой-либо из списков будет изменен).

Чтобы обойти это, вы можете быстро загрузить результат, используя метод Linq .ToArray().

Итак, теперь, если вы хотите пройтись по всему дереву, вы просто делаете:

foreach(var obj in the_root_object.AndChildren())
{

}

Предположим, the_root_object - это экземпляр CustomObject, который, как вы выразились, имеет различные «вены».

Это должно быть слегка переписано, если у вас есть конкретное требование к порядку отображения объектов относительно их дочерних элементов.

1 голос
/ 12 декабря 2011

Я бы просто использовал цикл и рекурсию, намного проще не может!

private List<CustomObject> GetChildObjects(CustomObject)
{
   List<CustomObject> retList = CustomObject.Childs;

   foreach(CustomerObject obj in retList)
   {
      retList.AddRange(GetChildObjects(obj));
   }

   return retList;
}
0 голосов
/ 12 декабря 2011

Если вы не заинтересованы в поддержании иерархии этих объектов, но только после получения всех их (я не смог бы разобрать из вашего поста, если вы есть), вы также можете рассмотреть возможность использования Reflection для получения всех объектов Тип CustomObject:

List<CustomObject> Objects = new List<CustomObject>();

Assembly asm = Assembly.LoadFrom(assemblyPath);
Type[] types = asm.GetTypes();

foreach (var t in types)
{
    if (t.IsClass && t.IsSubclassOf(typeof(CustomObject)))
    {
        var instance = (CustomObject) Activator.CreateObject(t);

        if (!Objects.Contains(instance))
            Objects.Add(instance);
    }
}

Особенно в больших приложениях выполнение большого количества циклов может оказать огромное влияние на производительность, и использование Reflection, как правило, является хорошей альтернативой.

...