迭代器模式

迭代器模式(Iterator Pattern)


模式定义

迭代器模式是一种行为型设计模式,它提供一种方法顺序访问一个聚合对象中的各个元素,而无需暴露该对象的内部表示。

核心思想

  • 封装遍历逻辑:将集合的遍历行为抽象为独立的迭代器对象,避免暴露集合内部结构。
  • 职责分离:集合负责管理元素,迭代器负责遍历元素,符合单一职责原则。
  • 统一接口:为不同的集合提供一致的遍历方式(如数组、链表、树等)。

模式结构

迭代器模式包含以下核心角色:

角色 描述
IIterator 迭代器接口,定义遍历方法(如 MoveNext, Current)。
IAggregate 聚合接口,定义创建迭代器的方法(如 GetIterator)。
ConcreteAggregate 具体聚合类,实现聚合接口,存储数据(如 BookCollection)。
ConcreteIterator 具体迭代器,实现迭代器接口,封装遍历逻辑(如 BookIterator)。

代码示例

1. 定义迭代器与聚合接口

// 迭代器接口
public interface IIterator<T>
{
    bool MoveNext();
    T Current { get; }
}

// 聚合接口
public interface IAggregate<T>
{
    IIterator<T> GetIterator();
}

2. 实现具体聚合类(书籍集合)

public class BookCollection : IAggregate<Book>
{
    private List<Book> _books = new List<Book>();

    public void Add(Book book) => _books.Add(book);

    // 返回具体迭代器
    public IIterator<Book> GetIterator() => new BookIterator(_books);
}

public class Book
{
    public string Title { get; set; }
    public string Author { get; set; }
}

3. 实现具体迭代器

public class BookIterator : IIterator<Book>
{
    private readonly List<Book> _books;
    private int _position = -1;

    public BookIterator(List<Book> books) => _books = books;

    public bool MoveNext()
    {
        return _books.Count> ++_position;
    }

    public Book Current => (_position >= 0 && _position < _books.Count)
                           ? _books[_position]
                           : throw new InvalidOperationException();
}

4. 使用迭代器遍历集合

var collection = new BookCollection();
collection.Add(new Book { Title = "Design Patterns", Author ="F" });
collection.Add(new Book { Title = "Clean Code", Author = "Robert C. Martin" });

IIterator<Book> iterator = collection.GetIterator();
while (iterator.MoveNext())
{
    Console.WriteLine($"Book: {iterator.Current.Title}, Author: {iterator.Current.Author}");
}

代码分析

1.接口分离

  • IIterator 和 IAggregate 接口确保遍历逻辑与集合解耦。
  • 客户端仅依赖接口,不关心具体集合类型(如 List 或 T[])。

2.遍历控制

  • MoveNext() 移动游标并返回是否存在下一个元素。
  • Current 属性验证当前游标位置,避免越界访问。

3.扩展性

  • 新增遍历方式(如逆序遍历)只需实现新的 ConcreteIterator,无需修改集合代码。

高级应用场景与扩展

1. 支持多种遍历策略

为同一集合提供不同遍历方式(如顺序、逆序、条件过滤),通过不同迭代器实现。

示例:逆序迭代器
public class ReverseBookIterator : IIterator<Book>
{
    private readonly List<Book> _books;
    private int _position;

    public ReverseBookIterator(List<Book> books)
    {
        _books = books;
        _position = _books.Count; // 从末尾开始
    }

    public bool MoveNext()
    {
        _position--;
        return _position >= 0;
    }

    public Book Current => (_position >= 0 && _position < _books.Count) 
                           ? _books[_position] 
                           : throw new InvalidOperationException();
}
使用方式
// 聚合类中新增逆序迭代器方法
public IIterator<Book> GetReverseIterator() => new ReverseBookIterator(_books);

2. 惰性加载与 yield return

利用 C# 的 yield return 实现按需加载数据,减少内存占用。

示例:分页加载数据
public class PagedBookCollection : IEnumerable<Book>
{
    private readonly int _pageSize;
    private readonly List<Book> _allBooks;

    public PagedBookCollection(List<Book> books, int pageSize = 5)
    {
        _allBooks = books;
        _pageSize = pageSize;
    }

    public IEnumerator<Book> GetEnumerator()
    {
        int currentPage = 0;
        while (true)
        {
            // 模拟分页加载(实际可能从数据库加载)
            var page = _allBooks.Skip(currentPage * _pageSize).Take(_pageSize).ToList();
            if (page.Count == 0) yield break;

            foreach (var book in page)
            {
                yield return book;
            }
            currentPage++;
        }
    }

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

3. 组合模式与树形结构遍历

迭代器模式常与组合模式结合,遍历树形结构(如文件系统、菜单树)。

示例:树节点遍历
public class TreeNode
{
    public string Name { get; set; }
    public List<TreeNode> Children { get; } = new List<TreeNode>();
}

// 深度优先迭代器
public class DepthFirstIterator : IIterator<TreeNode>
{
    private Stack<TreeNode> _stack = new Stack<TreeNode>();

    public DepthFirstIterator(TreeNode root)
    {
        _stack.Push(root);
    }

    public bool MoveNext()
    {
        if (_stack.Count == 0) return false;
        var current = _stack.Pop();
        foreach (var child in current.Children.Reverse())
        {
            _stack.Push(child);
        }
        return true;
    }

    public TreeNode Current => _stack.Peek();
}

C# 迭代器的底层机制​

1. IEnumerable 与 IEnumerator
  • IEnumerable​​:声明集合可被遍历(通过 GetEnumerator 方法)。
  • IEnumerator​​:提供遍历的游标控制(MoveNext、Current、Reset)。
2. 编译器生成的状态机​

使用 yield return 时,编译器会自动生成一个状态机类,保存当前迭代位置和局部变量。

反编译后的 yield 代码
// 编译器生成的代码(简化)
public class GeneratedEnumerator : IEnumerator<Book>
{
    private int _state;
    private Book _current;
    private List<Book> _books;
    private int _index;

    public Book Current => _current;

    public bool MoveNext()
    {
        switch (_state)
        {
            case 0:
                _index = 0;
                _state = 1;
                goto case 1;
            case 1:
                if (_index < _books.Count)
                {
                    _current = _books[_index];
                    _index++;
                    _state = 1;
                    return true;
                }
                _state = -1;
                return false;
            default:
                return false;
        }
    }
}

迭代器模式的常见问题

1. 线程安全问题
  • 问题:多个线程同时遍历同一集合可能导致状态冲突。
  • 解决方案:为每个线程创建独立的迭代器实例。
2. 遍历过程中的集合修改
  • 问题:在遍历时修改集合(如增删元素)可能导致异常(。
  • 解决方案:实现集合版本号检查(如 List 的 _version 字段)。
posted @ 2025-04-14 11:48  刘继先  阅读(57)  评论(0)    收藏  举报