如何实现IEnumerator< >复合模式的接口

本文关键字:复合 模式 接口 IEnumerator 何实现 实现 | 更新日期: 2023-09-27 18:03:24

我有一个遵循复合模式的对象集合。它们形成了一个树形结构,我想用IEnumerator遍历它。我正在翻译《Head First Design Patterns》一书中的一些java代码。我已经实现了两个实现IEnumerator接口的类:CompositeIteratorNullIterator

这是我想翻译成c#的java代码。

此外,这是我想要的Waitress类实现,当我只需要调用MoveNext()Current来遍历整个树结构时。

现在我的代码没有进入涉及迭代器的while循环,我想在控制台上打印素食MenuItem对象。 下面是我的代码:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace Iterator
{
  class Program
  {
    static void Main(string[] args)
    {
      MenuComponent pancakeHouseMenu = new Menu("PANCAKEHOUSE MENU", "Breakfast");
      MenuComponent dinerMenu = new Menu("DINER MENU", "Lunch");
      MenuComponent cafeMenu = new Menu("CAFE MENU", "Dinner");
      MenuComponent dessertMenu = new Menu("DESSERT MENU", "Dessert of course!");
      MenuComponent allMenus = new Menu("ALL MENUS", "All menus combined");
      allMenus.Add(pancakeHouseMenu);
      allMenus.Add(dinerMenu);
      allMenus.Add(cafeMenu);
      pancakeHouseMenu.Add(new MenuItem("K&B Pancake breakfast", "pancakes with scrambled eggs, and toast", true, 2.99));
      pancakeHouseMenu.Add(new MenuItem("Regular Pancake breakfast", "pancakes with fried eggs, sausage", false, 2.99));
      dinerMenu.Add(new MenuItem("Veggie burguer and air fries", "Veggie burguer on a whole wheat bun, lettuce, tomato and fries", true, 3.99));
      dinerMenu.Add(new MenuItem("Soup of the day", "Soup of the day with a side salad", false, 3.69));
      dinerMenu.Add(dessertMenu);
      dessertMenu.Add(new MenuItem("Apple pie", "Apple pie with a flakey crust, topped with vanilla ice cream", true, 1.59));
      cafeMenu.Add(new MenuItem("Vegetarian BLT", "(Fakin') Bacon with lettuce & tomato on whole wheat", true, 2.99));
      cafeMenu.Add(new MenuItem("BLT", "Bacon with lettuce & tomato on whole wheat", false, 2.99));
      Waitress waitress = new Waitress(allMenus);
      waitress.PrintVegetarianMenu();
    }
  }
  class Waitress
  {
    private MenuComponent AllMenus { get; set; }
    public Waitress(MenuComponent allMenus)
    {
      AllMenus = allMenus;
    }
    public void PrintMenu()
    {
      AllMenus.Print();
    }
    public void PrintVegetarianMenu()
    {
      CompositeIterator<MenuComponent> iterator = (CompositeIterator<MenuComponent>)AllMenus.CreateIterator();
      Console.WriteLine("VEGATARIAN MENU");
      // this loop is never entered
      while (iterator.MoveNext())
      {
        Console.WriteLine("inside while loop");
        MenuComponent menuComponent = (MenuComponent)iterator.Current;
        Console.WriteLine(menuComponent.Name);
        try
        {
          if (menuComponent.Vegetarian)
          {
            menuComponent.Print();
          }
        }
        catch (NotSupportedException e)
        {
          Console.WriteLine("Operation not supported.");
        }
      }
    }
  }
  /*
  Methods of MenuComponent class are virtual, because we sometimes want to use the default behavior. The CreateIterator method is abstract.
  */
  abstract class MenuComponent
  {
    // Composite methods
    public virtual void Add(MenuComponent menuComponent)
    {
      throw new NotSupportedException();
    }
    public virtual void Remove(MenuComponent menuComponent)
    {
      throw new NotSupportedException();
    }
    public virtual MenuComponent GetChild(int i)
    {
      throw new NotSupportedException();
    }
    // End of composite methods
    // Operation methods
    public virtual string Name
    {
      get
      {
        throw new NotSupportedException();        
      }
      set
      {
        throw new NotSupportedException();
      }
    }
    public virtual string Description
    {
      get
      {
        throw new NotSupportedException();        
      }
      set
      {
        throw new NotSupportedException();
      }
    }
    public virtual bool Vegetarian
    {
      get
      {
        throw new NotSupportedException();        
      }
      set
      {
        throw new NotSupportedException();
      }
    }
    public virtual double Price
    {
      get
      {
        throw new NotSupportedException();
      }
      set
      {
        throw new NotSupportedException();
      }
    }
    public virtual void Print()
    {
      throw new NotSupportedException();
    }
    // End of operation methods
    public abstract IEnumerator CreateIterator();
  }
  public sealed class CompositeIterator<T> : IEnumerator<T> {
    private readonly Stack<IEnumerator<T>> Stack = new Stack<IEnumerator<T>>();
    public CompositeIterator(IEnumerator<T> initial)
    {
      Stack.Push(initial);
    }
    public bool MoveNext()
    {
      while (Stack.Any())
      {
        if (!Stack.Peek().MoveNext())
        {
          Stack.Pop().Dispose();
          continue;
        }
        var tmp = Current as IEnumerable<T>;
        if (tmp != null) { Stack.Push(tmp.GetEnumerator()); }
      }
      return false;
    }
    public void Reset() { throw new NotSupportedException(); }
    public T Current => Stack.Peek() != null ? Stack.Peek().Current : default(T);
    object IEnumerator.Current => Current;
    public void Dispose()
    {
      if (!Stack.Any()) { return; }
      try {
        foreach (var x in Stack) {
          x.Dispose();
        }
      } catch { }
    }
  }
  public sealed class NullIterator<T> : IEnumerator<T> {
    public NullIterator() {}
    public bool MoveNext()
    {
      return false;
    }
    public void Reset() { throw new NotSupportedException(); }
    public T Current
    {
      get
      {
        return default(T);
      }
    }
    object IEnumerator.Current => Current;
    public void Dispose()
    {
      return;
    }
  }
  // This is a tree leaf
  class MenuItem : MenuComponent
  {
    public override string Name { get; set; }
    public override string Description { get; set; }
    public override bool Vegetarian { get; set; }
    public override double Price { get; set; }
    public MenuItem(string name, string description, bool vegetarian, double price)
    {
      Name = name;
      Description = description;
      Vegetarian = vegetarian;
      Price = price;
    }
    public override void Print()
    {
      Console.Write("  " + Name);
      if (Vegetarian)
      {
        Console.Write("(v)");
      }
      Console.Write(", " + Price);
      Console.Write("     -- " + Description);
    }
    public override IEnumerator CreateIterator()
    {
      return new NullIterator<MenuItem>();
    }
  }
  // This is a tree node
  class Menu : MenuComponent
  {
    public List<MenuComponent> MenuComponents;
    public override string Name { get; set; }
    public override string Description { get; set; }
    public Menu(string name, string description)
    {
      Name = name;
      Description = description;
      MenuComponents = new List<MenuComponent>();
    }
    public override void Add(MenuComponent menuComponent)
    {
      MenuComponents.Add(menuComponent);
    }
    public override void Remove(MenuComponent menuComponent)
    {
      MenuComponents.Remove(menuComponent);
    }
    public override MenuComponent GetChild(int i)
    {
      return MenuComponents[i];
    }
    // we have to use recursion to print all the hierarchy
    public override void Print()
    {
      Console.Write("'n" + Name);
      Console.WriteLine(", " + Description);
      Console.WriteLine("--------------");
      IEnumerator iterator = MenuComponents.GetEnumerator();
      while(iterator.MoveNext())
      {
        MenuComponent menuComponent = (MenuComponent)iterator.Current;
        menuComponent.Print();
        Console.Write("'n");
      }
    }
    public override IEnumerator CreateIterator()
    {
      return new CompositeIterator<MenuComponent>(MenuComponents.GetEnumerator());
    }
  }
}

如何实现IEnumerator< >复合模式的接口

这是我能得到的最接近你想要的而不需要创建我自己的迭代器的方法。我觉得没有必要重新编写已经存在于。net框架中的东西。

class Program
{
    static void Main(string[] args)
    {
        //var all = new Menu("ALL SECTIONS", "All menu sections");
        var pancakeHouseMenu = new Menu("PANCAKEHOUSE MENU", "Breakfast", 
            new MenuSection("PANCAKES", "Breakfast Pancakes Selection",
                new MenuItem("K&B Pancake breakfast", "pancakes with scrambled eggs, and toast", true, 2.99m),
                new MenuItem("Regular Pancake breakfast", "pancakes with fried eggs, sausage", false, 2.99m)));
        var dinnerMenu = new Menu("DINNER MENU", "Lunch",
            new MenuSection("","",
                new MenuItem("Veggie burger and air fries", "Veggie burguer on a whole wheat bun, lettuce, tomato and fries", true, 3.99m),
                new MenuItem("Soup of the day", "Soup of the day with a side salad", false, 3.69m)));
        var cafeMenu = new Menu("CAFE MENU", "Dinner",
            new MenuSection("", "",
                new MenuItem("Vegetarian BLT", "(Fakin') Bacon with lettuce & tomato on whole wheat", true, 2.99m),
                new MenuItem("BLT", "Bacon with lettuce & tomato on whole wheat", false, 2.99m)),
            new MenuSection("DESSERT MENU", "Dessert of course!",
                new MenuItem("Apple pie", "Apple pie with a flakey crust, topped with vanilla ice cream", true, 1.59m)));
        var waiter = new Waitress(pancakeHouseMenu, dinnerMenu, cafeMenu);
        //waiter.Print();
        //waiter.PrintVegenerian();

        WriteFileAndOpenNotepad(waiter.ToString());
        WriteFileAndOpenNotepad(waiter.ToVegetarianString());
    }
    static void WriteFileAndOpenNotepad(string text)
    {
        var fn = Path.GetTempFileName();
        fn=Path.ChangeExtension(fn, ".txt");
        File.WriteAllText(fn, text);
        Process.Start(fn);
    }
}


public abstract class MenuComponent
{
    public string Description { get; private set; }
    public string Name { get; private set; }
    protected MenuComponent(string name, string description)
    {
        this.Name=name;
        this.Description=description;
    }
    public abstract override string ToString();
    public void Print()
    {
        Console.WriteLine(ToString());
    }
}
public class MenuItem : MenuComponent
{
    public MenuItem(string name, string description, bool vegeterian, decimal price)
        : base(name, description)
    {
        this.Price=price;
        this.Vegetarian=vegeterian;
    }
    public decimal Price { get; set; }
    public bool Vegetarian { get; set; }
    public override string ToString()
    {
        //Use 28 columns for the item name
        return string.Format("{0,28}{1}, {2}     -- {3}",
            Name, Vegetarian ? "(v)" : "   ", Price, Description);
    }
}
public class MenuSection : MenuComponent
{
    public MenuSection(string name, string description, params MenuItem[] items)
        : this(name, description, items as IEnumerable<MenuItem>)
    { }
    public MenuSection(string name, string description, IEnumerable<MenuItem> items) : base(name, description)
    {
        this.Items=new List<MenuItem>(items);
    }
    public List<MenuItem> Items { get; private set; }
    public override string ToString()
    {
        var sb = new StringBuilder();
        sb.AppendLine();
        if(Name.Length>0)
        {
            sb.AppendFormat("{0}, {1}", Name, Description);
            sb.AppendLine();
            sb.AppendLine("--------------");
        }
        foreach(var item in Items)
        {
            sb.AppendLine();
            sb.AppendLine(item.ToString());
        }
        return sb.ToString();
    }
    public MenuSection VegeterianSections
    {
        get
        {
            var veg = Items.Where((item) => item.Vegetarian);
            return new MenuSection(Name, Description, veg);
        }
    }
}
public class Menu : MenuComponent
{
    public Menu(string name, string description, IEnumerable<MenuSection> sections)
        : base(name, description)
    {
        this.MenuSections=new List<MenuSection>(sections);
    }
    public Menu(string name, string description, params MenuSection[] sections)
        : this(name, description, sections as IEnumerable<MenuSection>)
    { }
    public List<MenuSection> MenuSections { get; private set; }
    public override string ToString()
    {
        var sb = new StringBuilder();
        sb.AppendLine();
        sb.AppendFormat("[{0}, {1}]", Name, Description);
        sb.AppendLine();
        sb.AppendLine("==============");
        foreach(var section in MenuSections)
        {
            sb.AppendLine();
            sb.AppendLine(section.ToString());
        }
        return sb.ToString();
    }
    public Menu VegeraterianMenu
    {
        get
        {
            return new Menu(Name, Description, MenuSections.Select((section)=> section.VegeterianSections));
        }
    }
}
public class Waitress
{
    public Waitress(params Menu[] all)
    {
        this.AllMenus=new List<Menu>(all);
        this.VegeratianMenu=all.Select((menu)=>menu.VegeraterianMenu).ToList();
    }
    public IList<Menu> AllMenus { get; private set; }
    public IList<Menu> VegeratianMenu { get; private set; }
    public override string ToString()
    {
        StringBuilder sb = new StringBuilder();
        sb.AppendLine("*** A L L  I T E M S ***");
        foreach(var item in AllMenus)
        {
            sb.AppendLine("************************");
            sb.AppendLine(item.ToString());
        }
        return sb.ToString();
    }
    public string ToVegetarianString()
    {
        StringBuilder sb = new StringBuilder();
        sb.AppendLine("*** V E G  I T E M S ***");
        foreach(var item in VegeratianMenu)
        {
            sb.AppendLine("************************");
            sb.AppendLine(item.ToString());
        }
        return sb.ToString();
    }
    public void Print()
    {
        Console.WriteLine(ToString());
    }
    public void PrintVegenerian()
    {
        Console.WriteLine(ToVegetarianString());
    }
}

在与c# freenode IRC频道上的人联系后,其中一人给了我一个比书中更好的解决方案,我在这里得到了答案。这是代码,有了这个答案,我希望清楚我想要什么:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace Iterator
{
  class Program
  {
    static void Main(string[] args)
    {
      MenuComponent pancakeHouseMenu = new Menu("PANCAKEHOUSE MENU", "Breakfast");
      MenuComponent dinerMenu = new Menu("DINER MENU", "Lunch");
      MenuComponent cafeMenu = new Menu("CAFE MENU", "Dinner");
      MenuComponent dessertMenu = new Menu("DESSERT MENU", "Dessert of course!");
      MenuComponent allMenus = new Menu("ALL MENUS", "All menus combined");
      allMenus.Add(pancakeHouseMenu);
      allMenus.Add(dinerMenu);
      allMenus.Add(cafeMenu);
      pancakeHouseMenu.Add(new MenuItem("K&B Pancake breakfast", "pancakes with scrambled eggs, and toast", true, 2.99));
      pancakeHouseMenu.Add(new MenuItem("Regular Pancake breakfast", "pancakes with fried eggs, sausage", false, 2.99));
      dinerMenu.Add(new MenuItem("Veggie burguer and air fries", "Veggie burguer on a whole wheat bun, lettuce, tomato and fries", true, 3.99));
      dinerMenu.Add(new MenuItem("Soup of the day", "Soup of the day with a side salad", false, 3.69));
      dinerMenu.Add(dessertMenu);
      dessertMenu.Add(new MenuItem("Apple pie", "Apple pie with a flakey crust, topped with vanilla ice cream", true, 1.59));
      cafeMenu.Add(new MenuItem("Vegetarian BLT", "(Fakin') Bacon with lettuce & tomato on whole wheat", true, 2.99));
      cafeMenu.Add(new MenuItem("BLT", "Bacon with lettuce & tomato on whole wheat", false, 2.99));
      cafeMenu.Add(dessertMenu);
      Waitress waitress = new Waitress(allMenus);
      waitress.PrintVegetarianMenu();
    }
  }
  class Waitress
  {
    private MenuComponent AllMenus { get; set; }
    public Waitress(MenuComponent allMenus)
    {
      AllMenus = allMenus;
    }
    public void PrintMenu()
    {
      AllMenus.Print();
    }
    public void PrintVegetarianMenu()
    {
      Console.WriteLine("VEGATARIAN MENU");
      foreach (MenuComponent menuComponent in AllMenus)
      {
        try
        {
          if (menuComponent.Vegetarian)
          {
            menuComponent.Print();
            Console.Write("'n");
          }
        }
        catch (NotSupportedException)
        {
          Console.WriteLine("Operation not supported.");
        }
      }
    }
  }
  abstract class MenuComponent : IEnumerable<MenuComponent>
  {
    // Composite methods
    public abstract void Add(MenuComponent menuComponent);
    public abstract void Remove(MenuComponent menuComponent);
    public abstract MenuComponent GetChild(int i);
    // End of composite methods
    // Operation methods
    public virtual string Name { get; set; }
    public virtual string Description { get; set; }
    public virtual bool Vegetarian { get; set; }
    public virtual double Price { get; set; }
    public abstract void Print();
    public abstract IEnumerator<MenuComponent> GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
  }
  /// This is a tree leaf
  class MenuItem : MenuComponent
  {
    public MenuItem(string name, string description, bool vegetarian, double price)
    {
      Name = name;
      Description = description;
      Vegetarian = vegetarian;
      Price = price;
    }
    public override void Print()
    {
      Console.Write("  " + Name);
      if (Vegetarian)
      {
        Console.Write("(v)");
      }
      Console.Write(", " + Price);
      Console.Write("     -- " + Description);
    }
    public override IEnumerator<MenuComponent> GetEnumerator()
    {
      yield break;
    }
    public override void Add(MenuComponent menuComponent)
    {
      throw new NotSupportedException();
    }
    public override void Remove(MenuComponent menuComponent)
    {
      throw new NotSupportedException();
    }
    public override MenuComponent GetChild(int i)
    {
      throw new NotSupportedException();
    }
  }
  /// This is a tree node
  class Menu : MenuComponent
  {
    private List<MenuComponent> MenuComponents;
    public Menu(string name, string description)
    {
      Name = name;
      Description = description;
      MenuComponents = new List<MenuComponent>();
    }
    public override void Add(MenuComponent menuComponent)
    {
      MenuComponents.Add(menuComponent);
    }
    public override void Remove(MenuComponent menuComponent)
    {
      MenuComponents.Remove(menuComponent);
    }
    public override MenuComponent GetChild(int i)
    {
      return MenuComponents[i];
    }
    // we have to use recursion to print all the hierarchy
    public override void Print()
    {
      Console.Write("'n" + Name);
      Console.WriteLine(", " + Description);
      Console.WriteLine("--------------");
      foreach (MenuComponent menuComponent in MenuComponents)
      {
        menuComponent.Print();
        Console.Write("'n");
      }
    }
    public override IEnumerator<MenuComponent> GetEnumerator()
    {
      var components = new Stack<MenuComponent>(new[] { this });
      while (components.Any())
      {
        MenuComponent component = components.Pop();
        yield return component;
        var menu = component as Menu;
        if (menu != null)
        {
          foreach (var n in menu.MenuComponents) components.Push(n);
        }
      }
    }
  }
}