复合迭代失败(.net)

时间:2011-03-20 14:54:45

标签: c# .net linq design-patterns composite

以下是使用复合模式的第一次尝试。

它的工作原理是我可以任意嵌套并获得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);
        }
    }

1 个答案:

答案 0 :(得分:3)

  

如何更改现有内容   迭代代码,以防止此错误?

发生异常是因为Children属性的getter中的代码正在修改集合,而迭代它。

您似乎认为代码

var result = _children;

创建_children字段引用的列表的副本。它没有,它只是将引用复制到列表中(这是字段的值所代表的值)变量。

复制列表的简单方法是:

var result = _children.ToList();
  

我知道如何将其转换为Linq   等效。

当前代码的LINQ等价物应该以惰性方式工作:

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

编辑: 我最初的印象是你的代码将遍历深度限制在两个级别(子级和孙级),但现在我可以看到情况并非如此:确实有一个递归调用属性 Children而不仅仅是字段 _children的值。这个命名很混乱,因为属性和“后备”字段完全代表不同的东西。我强烈建议您将属性重命名为更有意义的内容,例如Descendants