2017-04-07 47 views
4

我如下实施IEnumerable的用于

public interface IComponent 
{ 
    string Name { get; } 
} 

public interface IComposite : IComponent 
{ 
    void AddRange(IEnumerable<IComponent> components); 
} 
public interface ILeaf : IComponent 
{ 
    string Content { get; } 
    string Parent { get; } 
} 

public class Composite : IComposite 
{ 
    // return an iterator? 
    private readonly List<IComponent> _children = new List<IComponent>(); 

    public Composite(string name) 
    { 
     Name = name; 
    } 

    public string Name { get; } 

    public void AddRange(IEnumerable<IComponent> components) 
    { 
     _children.AddRange(components); 
    } 
} 

public class Leaf : ILeaf 
{ 
    public string Name { get; } 
    public string Content { get; } 
    public string Parent { get; } 

    public Leaf(string name, string content, string parent) 
    { 
     Name = name; 
     Content = content; 
     Parent = parent; 
    } 
} 

我已经填充了复合从XML文件如下

var collection = XElement.Load(@"C:\somexml.xml"); 
    var composite = CreateComposite(collection); 

其中

实现的复合图案组合模式的叶节点
public IComponent CreateComposite(XElement element) 
    { 
     if (!element.HasElements) 
      return new Leaf(element.Name.LocalName, element.Value, element.Parent.Name.LocalName); 

     var composite = new Composite(element.Name.LocalName); 
     composite.AddRange(element.Elements().Select(CreateComposite)); 
     return composite; 
    } 

这会像预期的那样填充我的复合材料 - 非常棒!不过,我现在希望我的组合通过IEnumerable的实现返回一个迭代器。于是我通过组件的顶层试过这种

public class Composite : IComposite, IEnumerable<IComponent> 
{ 
    // return an iterator? 
    private readonly List<IComponent> _children = new List<IComponent>(); 

    public Composite(string name) 
    { 
     Name = name; 
    } 

    public string Name { get; } 

    public void AddRange(IEnumerable<IComponent> components) 
    { 
     _children.AddRange(components); 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 

    public IEnumerator<IComponent> GetEnumerator() 
    { 
     foreach (var child in _children) 
     { 
      yield return child; 
     } 
    } 
} 

但这只是迭代,即,将不返回嵌套在_children任何组件。我如何更新这个递归遍历所有组件?

回答

3

您可以递归遍历这样的(它会做深度优先的方式迭代):

public interface IComposite : IComponent, IEnumerable<IComponent> 
{ 
    void AddRange(IEnumerable<IComponent> components); 
} 

public IEnumerator<IComponent> GetEnumerator() 
{ 
    foreach (var child in _children) 
    { 
     yield return child; 
     var composite = child as IComposite; 
     if (composite != null) { 
      foreach (var sub in composite) { 
       yield return sub; 
      } 
     } 
    } 
} 

如果你想避免投给IComposite - 你需要重新设计你的接口,使您的Composite将保留另一个IComposite的列表,而不是组件。然后ILeft也将变成IComposite虚拟实现。

+0

所以我改变了我的接口,使得'IComponent'实现IEnumerable ',然后在没有强制转换的情况下使用你的代码,在'Leaf'中使用'yield break'来实现'GetEnumerator'(见[here](http:/ /stackoverflow.com/questions/1714351/return-an-empty-ienumerator))。这为我提供了深度优先递归 - 非常棒! –

3

你可以使用Linq递归地实现遍历,如下所示。

public IEnumerable<IComponent> GetSuccessors() 
{ 
    return _children 
      .Concat(_children.SelectMany(iChild => iChild.GetSuccessors()); 
} 

如果需要depht-first traversal,则可以使用以下实现。

public IEnumerable<IComponent> GetSuccessors() 
{ 
    return _children 
      .SelectMany(iChild => new IComponent[]{iChild}.Concat(iChild.GetSuccessors())); 
} 

或者,如果您需要使用初始语法,则可以使用以下语句。

public IEnumerator<IComponent> GetEnumerator() 
{ 
    var Successors 
     = _children 
      .SelectMany(iChild => new IComponent[]{iChild}.Concat(iChild.GetSuccessors())); 
    foreach (var iSuccessor in Successors) 
    { 
     yield return iSuccessor; 
    } 
} 
+0

这是非常有用的信息,并且非常有用,可以看到解决问题的不同方法(将这些知识存入银行!)。 –