以下是使用复合模式的第一次尝试。
它的工作原理是我可以任意嵌套并获得Duration属性的正确结果,这是组合的重点。但是有一个编码问题,因为需要输出复合的ToString()的子代的迭代失败:
System.InvalidOperationException : Collection was modified; enumeration operation may not execute.
这是posting中GetDescendents的一些扩展方法,包括使用堆栈来避免递归和 嵌套迭代器。
我想首先理解这种模式,所以我在这里有几个问题:
任何有关工作(非trival).net代码示例的良好链接也将非常受欢迎。
干杯,
Berryl
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);
}
}
答案 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
。