2017-04-07 32 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

這是非常有用的信息,並且非常有用,可以看到解決問題的不同方法(將這些知識存入銀行!)。 –