代码之家  ›  专栏  ›  技术社区  ›  Jay

如何迭代实现IEnumerator<T>的自定义集合?

  •  0
  • Jay  · 技术社区  · 2 年前

    我想使用C#迭代我的自定义集合的值。

    我创建了一个名为的集合 MyLinkedList 表示这样的自定义链接列表

    public class MyLinkedList<T> : IEnumerable<T?> where T : IEquatable<T?>
    {
        // other methods are removed for simplicity. 
    
        IEnumerator<T?> IEnumerable<T?>.GetEnumerator()
            => new MyLinkedListNodeEnumerator<T>(_root);
    
        public IEnumerator GetEnumerator()
            => new MyLinkedListNodeEnumerator<T>(_root);
    }
    

    我实施了 IEnumerable<T?> 接口,所以我可以使用 foreach 循环来迭代我的自定义集合中的值。以下是的实现 MyLinkedListNodeEnumerator

    public class MyLinkedListNodeEnumerator<T>(MyLinkedListNode<T>? root)
        : IEnumerator<T?> where T : IEquatable<T?>
    {
        private readonly MyLinkedListNode<T>? _root = root;
    
        private MyLinkedListNode<T>? _current = root;
    
        public object? Current
        {
            get
            {
                var current = _current;
    
                _current = _current!.Next;
    
                return current;
            }
        }
    
        T? IEnumerator<T?>.Current
        {
            get
            {
                var current = _current;
    
                _current = _current!.Next;
    
                return current!.Data;
            }
        }
    
        public void Dispose()
        {
    
        }
    
        public bool MoveNext()
            => _current?.Next != null;
    
        public void Reset()
        {
            _current = _root;
        }
    }
    

    这里是一个用例示例,

    var list = new MyLinkedList<int>();
    list.InsertFirst(10);
    list.InsertNext(20);
    list.InsertNext(30);
    

    当我在Enumerator对象上循环时,我希望每个项都是 int 类型,因为这是创建列表的泛型类型。所以 前臂 正在使用 IEnumerator GetEnumerator() IEnumerable<T?>.GetEnumerator() .

    foreach(var item in list) 
    {
        // here item is an object of a type MyLinkedListNode<int> 
        // not int as I am expecting.
    }
    

    我如何确保 前臂 将使用 IEnumerable<T>。GetEnumerator() 这样我就可以迭代 int 值?

    1 回复  |  直到 2 年前
        1
  •  2
  •   poke    2 年前

    不知道你的具体情况 MyLinkedListNode :

    public class MyLinkedListNodeEnumerator<T> : IEnumerator<T>
    {
        private MyLinkedListNode<T> _root;
        private MyLinkedListNode<T> _current = null;
    
        public MyLinkedListNodeEnumerator(MyLinkedListNode<T> root)
            => _root = root;
    
        public T Current
            => _current.Data;
    
        object IEnumerator.Current
            => _current.Data;
    
        public bool MoveNext()
        {
            _current = _current is null ? _root : _current.Next;
            return _current is not null;
        }
    
        public void Reset()
            => _current = null;
    
        public void Dispose()
        { }
    }
    

    请注意 Current 是幂等的,不应修改枚举器。向前移动枚举器的唯一方法是使用 MoveNext 。在初始状态下,枚举器应该启动 之前 第一个因素,这就是为什么 _current 开始于 null 。此外,访问 现在的 没有以前的成功 移到下一行 未定义调用。


    为了使您的枚举器在默认情况下是通用的,您应该围绕的实现进行交换 IEnumerable<T> :

    public class MyLinkedList<T> : IEnumerable<T>
    {
        // …
    
        // generic version is public without an explicit implementation
        public IEnumerator<T> GetEnumerator()
            => new MyLinkedListNodeEnumerator<T>(_root);
    
        // explicit implementation on the non-generic one
        IEnumerator IEnumerable.GetEnumerator()
            => new MyLinkedListNodeEnumerator<T>(_root);
    }