2011-12-12 22 views
3

我有一个具有n个Children的CustomObject。这些孩子是CustomObjects列表。事情是这样的:如何从n个Children/Subchildren等获得一个CustomObject的所有子对象

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

我所寻找的是最高效的方式来获得所有N儿童和他们的孩子和subchildren等,从CustomObject的单个实例。有没有比循环遍历所有veigns更好的方法,直到我到达最后(空)?

(C#,.NET 3.5)

为了使它更清楚,我会做出了榜样结构:

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

在这种情况下,我需要得到根对象下的所有自定义对象。

+1

我看不出有什么毛病循环和递归此实例。可能有一些花哨的LINQ可以应用于某人可能的贡献,但除此之外,简单的循环和递归似乎对我来说完全适合。 – ThePower

回答

6

不,你只需循环一切。您可以使用递归方法:

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); 

注:孩子的复数形式是孩子

2

可能不是,但它完全取决于你是如何做到的。你可以使用递归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_objectCustomObject的实例有各种各样的“脉”,因为你已经把它。

如果您对对象应该相对于其子女出现的顺序有特定要求,则必须稍微重写。

+0

谨防嵌套的收益率,http://stackoverflow.com/questions/1043050/c-sharp-performance-of-nested-yield-in-a-tree –

+0

@克里斯 - 同意,如果我们有一个又大又深,树那么可能会出现问题,在这种情况下,可能通过基于单个堆栈的解决方案进行热切平坦化将会好得多。 –

+0

@克里斯 - 这么说,不过,我看不出这是不是因为拼合的解决方案张贴塞巴斯蒂安一样好 - 他们表面上是相同的(除了一个事实,我没有把它写成一个扩展,轻松完成) - 如果你使用'ToArray',我也会做同样的事情。无论是否使用收益率,任何递归解决方案都会有相同的问题。 –

3

我使用这个扩展方法:

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); 
} 
+0

我喜欢这个解决方案,但它应该是可调用一个T,添加T,并且不需要如果每个项目包含已经被检查。如果目标是每个类型只有一个实例,那么应该使用Set来代替。 –

+0

我想我有一个丑陋的覆盖,如果neccesary,它把它添加到列表中添加根T和然后调用压平了。在设定好一点,我想这可以用一个HashSet,而不是列表 –

1

我只是使用循环和递归,不能变得更简单!

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

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

    return retList; 
} 
+0

的这将使一个例外,因为被列举的项目更改 –

2

你将不得不遍历每个童车收藏。您可以考虑这样的事情但是:

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; 
      } 
     } 
    } 
} 
0

如果你不感兴趣,维持这些对象的层次结构,但只有在获得所有这些之后,(我不能从您的文章做出来,如果你是) ,你也可以考虑使用反射来获得类型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); 
    } 
} 

特别是在较大的应用程序,运行大量的循环可能会对性能产生巨大的影响,并使用反射通常是一个很好的选择。

相关问题