2011-03-20 79 views
1

下面是使用複合模式的第一次嘗試。複合迭代失敗(.net)

它的工作原理是,我可以隨意嵌套並獲得Duration屬性的正確結果,並且是合成的焦點。但在一個編碼問題迭代超過所需輸出複合的ToString()孩子失敗:

System.InvalidOperationException : Collection was modified; enumeration operation may not execute. 

的是在這個posting GetDescendents一些擴展方法,包括使用堆棧,以避免一個花費遞歸和嵌套迭代器。

我想了解的圖案最好先的,所以我有幾個問題在這裏:

  • 我怎樣才能改變現有的重複代碼,以防止這種錯誤?我知道如何將它轉換爲Linq等價物,但我想把它作爲循環直到我明白它有什麼問題。
  • 在Composite中提供Count屬性還是以某種方式緩存迭代後的計數是典型的?
  • 在您不需要專門收藏的一般情況下,您通常會將您的Children屬性設置爲IEnumerable,IList還是List?

任何工作(非trival).net代碼示例的良好鏈接也將非常感激。

乾杯,
Berryl

CODE

public interface IComponent { 
    void Adopt(IComponent node); 
    void Orphan(IComponent node); 

    TimeSpan Duration { get; } 
    IEnumerable<IComponent> Children { get; } 
} 

public class Allocation : Entity, IAllocationNode { 

    public void Adopt(IAllocationNode node) { throw new InvalidOperationException(_getExceptionMessage("Adopt", this, node)); } 
    public void Orphan(IAllocationNode node) { throw new InvalidOperationException(_getExceptionMessage("Orphan", this, node)); } 

    public IEnumerable<IAllocationNode> Allocations { get { return Enumerable.Empty<IAllocationNode>(); } } 

    public virtual TimeSpan Duration { get; set; } 
} 


class MyCompositeClass : IAllocationNode { 
     public MyCompositeClass() { _children = new List<IAllocationNode>(); } 

     public void Adopt(IAllocationNode node) { _children.Add(node); } 
     public void Orphan(IAllocationNode node) { _children.Remove(node); } 

     public TimeSpan Duration { 
      get { 
       return _children.Aggregate(TimeSpan.Zero, (current, child) => current + child.Duration); 
      } 
     } 

     public IEnumerable<IAllocationNode> Children { 
      get { 
       var result = _children; 
       foreach (var child in _children) { 
        var childOnes = child.Children; 
        foreach (var node in childOnes) { 
         result.Add(node); 
        } 
       } 
       return result; 
      } 
     } 
     private readonly IList<IAllocationNode> _children; 

     #endregion 

     public override string ToString() { 
      var count = Children.Count(); 
      var hours = Duration.TotalHours.ToString("F2"); 
      return string.Format("{0} allocations for {1} hours", count, hours); 
     } 
    } 

回答

3

我怎樣才能改變現有的 迭代代碼,以防止這種錯誤?

異常發生,因爲在Children屬性的getter代碼被修改的集合循環訪問它。

你似乎有這樣一種印象,代碼

var result = _children; 

創建列表_children場稱爲下副本。它沒有,它只是將參考複製到變量的列表(這是該字段的值代表什麼)。

一個簡單的辦法,以列表拷貝過來是不是做的事:

var result = _children.ToList(); 

我知道如何將它轉換成一個LINQ 等同。

的LINQ相當於您當前密碼的,應在一個懶惰的方式工作,就是:

return _children.Concat(_children.SelectMany(child => child.Children)); 

編輯: 我本來是你的代碼被限制遍歷深度的印象兩個級別(子女和孫子女),但現在我可以看到情況並非如此:確實有遞歸調用屬性Children而不僅僅是字段_children的值。這個命名非常混亂,因爲屬性和'後援'字段完全代表不同的事情。我強烈建議您將該屬性重命名爲更有意義的內容,例如Descendants

+0

@Ari。是的,這解決了它。我沒有看到我限制遍歷的深度 - 你將如何修改這個以獲得所有的後代? – Berryl 2011-03-20 15:13:52

+0

拼寫錯了你的名字,對不起。我沒有看到我在限制遍歷的深度 - 你會如何修改這個以獲得所有的後代?乾杯 – Berryl 2011-03-20 15:20:22

+0

@Berryl:你說得對;我已經進行了編輯。 – Ani 2011-03-20 15:21:38