Ниже приведена первая попытка использования составного паттерна.
Это работает в том смысле, что я могу произвольно вкладывать и получать правильные результаты для свойства Duration с фокусом композиции. НО проблема кодирования в том, что итерация дочерних элементов, необходимых для вывода ToString () композита, завершается неудачно:
System.InvalidOperationException : Collection was modified; enumeration operation may not execute.
Это несколько методов расширения для GetDescendents в этой публикации , включая тот, который использует стек, чтобы избежать затрат на рекурсию и
вложенные итераторы.
Хотелось бы сначала лучше понять шаблон, поэтому у меня есть несколько вопросов здесь:
- Как я могу изменить существующий код итерации, чтобы предотвратить эту ошибку? Я знаю, как преобразовать его в эквивалент Linq, но я хочу оставить его как цикл, пока не пойму, что с ним не так.
- Типично в Composite предоставлять свойство Count или каким-то образом кэшировать счет после итерации?
- В общем случае, когда вам не нужна специализированная коллекция, вы обычно хотите, чтобы свойство Children было IEnumerable, IList или List?
Любые хорошие ссылки с примерами рабочего (нетривиального) кода .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);
}
}