Implementing IEnumerable for Leaf Nodes of Composite Pattern
我已经实现了如下的复合模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | 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文件中填充了组合,如下所示
1 2 | var collection = XElement.Load(@"C:\somexml.xml"); var composite = CreateComposite(collection); |
哪里
1 2 3 4 5 6 7 8 9 | 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的实现返回一个迭代器。所以我尝试了这个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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; } } } |
但这只会迭代组件的顶层,即不会返回嵌套在
您可以像这样递归迭代(它将以深度优先的方式进行迭代):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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; } } } } |
如果您想避免强制转换到
您可以使用linq递归实现遍历,如下所示。
1 2 3 4 5 | public IEnumerable<IComponent> GetSuccessors() { return _children .Concat(_children.SelectMany(iChild => iChild.GetSuccessors()); } |
如果需要depht first遍历,可以使用以下实现。
1 2 3 4 5 | public IEnumerable<IComponent> GetSuccessors() { return _children .SelectMany(iChild => new IComponent[]{iChild}.Concat(iChild.GetSuccessors())); } |
或者,如果您需要使用初始语法,您可以使用以下内容。
1 2 3 4 5 6 7 8 9 10 | public IEnumerator<IComponent> GetEnumerator() { var Successors = _children .SelectMany(iChild => new IComponent[]{iChild}.Concat(iChild.GetSuccessors())); foreach (var iSuccessor in Successors) { yield return iSuccessor; } } |