C#枚举集合、IEnumerable接口、IEnumerator接口、手动实现枚举器、使用迭代器来定义枚举器

本文探讨了C#中的枚举概念,强调了foreach语句在可枚举集合中的应用。枚举器通过实现IEnumerable接口和IEnumerator接口使集合可遍历。文中详细解释了如何手动实现枚举器,特别提到了yield关键字在迭代器中的作用,它允许在运行时生成值,简化了枚举器的实现。此外,还展示了如何通过迭代器为类定义枚举器,以按不同顺序输出数据。

枚举聚合中的元素

之前我们写过这样的代码:
在这里插入图片描述
foreach极大简化了需要编写的代码,但是foreach只能在特定情况下使用-只能遍历可枚举集合.
什么是可枚举集合?就是实现了System.Collections.IEnumerable接口的集合
在这里插入图片描述
可以看到IEnumerable接口包含了一个名为GetEnumerator的方法:
GetEnumerator返回IEnumerator
也就是返回了实现了IEnumerator接口的枚举器对象
在这里插入图片描述
可将枚举器视为指向列表中的元素的指针.指针最开始指向第一个元素之前的位置。
调用MoveNext方法,就可以让指针移到列表中的下一项,移动成功返回true,否则返回false。
Current属性访问当前指向的项,Rest方法返回到指针第一项之前的位置。
使用集合的GetEnumerator方法创建枚举器,然后反复调用MoveNext方法,并获取Current属性的值,就可以每次在集合中移动一个元素的位置。这就是foreach语句做的事情。

为了创建自己的可枚举集合类,就必须在自己的集合类中实现IEnumerable接口,并提供IEnumerator该接口的一个实现.
以便由集合类的GetEnumerator方法返回.

下面来手动实现枚举器
在这里插入图片描述
代码如下:


        static void InsertIntoTree<TItem>(ref Tree<TItem> tree, params TItem[] data) where TItem : IComparable<TItem>
        {
            foreach (TItem datum in data)
            {
                if (tree == null)
                {
                    tree = new Tree<TItem>(datum);
                }
                else
                {
                    tree.Insert(datum);
                }
            }
        }
            public class TreeEnumerator<TItem> : IEnumerator<TItem> where TItem : IComparable<TItem>
    {

        private Tree<TItem> currentData = null; //容纳对要枚举的树的引用
        private TItem currentItem = default(TItem);//容纳Current属性返回的值,因为TItem是未知的,我们不知道用什么值来初始化它
        //所以最好的方法就是使用default,如果是引用类型则为null,如果是数值就为0,如果是bool,则为false等等.

        private Queue<TItem> enumData = null;// 用节点的值填充enumData队列

        public TreeEnumerator(Tree<TItem> data) //构造器
        {
            this.currentData = data;
        }
        private void populate(Queue<TItem>enumQueue, Tree<TItem> tree)//填充方法
        {
            if(tree.LeftTree != null)
            {
                populate(enumQueue, tree.LeftTree);
            }
            enumQueue.Enqueue(tree.NodeData);
            if(tree.RightTree != null)
            {
                populate(enumQueue, tree.RightTree);
            }
        }

        TItem IEnumerator<TItem>.Current
        {
            get
            {
                if(this.enumData == null)//确定已经调用了一次MoveNext,因为枚举器最开始指向第一个元素之前的位置.
                {
                    throw new InvalidOperationException();
                }
                return this.currentItem;
            }
        }

        object IEnumerator.Current => throw new NotImplementedException();

        void IDisposable.Dispose()
        {
           // throw new NotImplementedException();
        }

        bool IEnumerator.MoveNext()
        {
            if(this.enumData == null)
            {
                this.enumData = new Queue<TItem>();
                populate(this.enumData, this.currentData);
            }
            if(this.enumData.Count > 0)
            {
                this.currentItem = this.enumData.Dequeue();
                return true;
            }
            return false;
        }

        void IEnumerator.Reset()
        {
            throw new NotImplementedException();
        }
    }
    public class Tree<TItem> : IEnumerable<TItem> where TItem : IComparable<TItem>
    {
        public TItem NodeData { get; set; }
        public Tree<TItem> LeftTree { get; set; }
        public Tree<TItem> RightTree { get; set; }

        public Tree(TItem nodeValue) //构造器名称不能包含类型参数,它名为Tree
        {
            this.NodeData = nodeValue;
            this.LeftTree = null;
            this.RightTree = null;
        }
        public void Insert(TItem newItem)
        {
            TItem currentNodeValue = this.NodeData;
            if (currentNodeValue.CompareTo(newItem) > 0) //比较当前节点的值和新项的值
            {
                if (this.LeftTree == null)
                {
                    this.LeftTree = new Tree<TItem>(newItem);
                }
                else
                {
                    this.LeftTree.Insert(newItem);
                }
            }
            else //右子树
            {
                if (this.RightTree == null)
                {
                    this.RightTree = new Tree<TItem>(newItem);
                }
                else
                {
                    this.RightTree.Insert(newItem);
                }
            }
        }

        public string WalkTree() //树的遍历
        {
            string result = "";
            if (this.LeftTree != null)
            {
                result = this.LeftTree.WalkTree();
            }
            result += $" {this.NodeData.ToString()}";
            if (this.RightTree != null)
            {
                result += this.RightTree.WalkTree();
            }
            return result;
        }

        IEnumerator<TItem> IEnumerable<TItem>.GetEnumerator()
        {
            return new TreeEnumerator<TItem>(this);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            throw new NotImplementedException();

        }
    }


用迭代器实现枚举器

为了减轻程序员的负担,C#提供了迭代器来帮我们更简单的让集合变得可枚举.
迭代器是能生成已排序值序列的一个代码块。

一个简单的迭代器
    class BasicCoolection<T> : IEnumerable<T>
    {
        private List<T> data = new List<T>();
        public void FillList(params T[] items)
        {
            foreach(var datum in items)
            {
                data.Add(datum);
            }
        }

        IEnumerator<T> IEnumerable<T>.GetEnumerator()
        {
            foreach(var datum in data)
            {
                yield return datum;
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            throw new NotImplementedException();
        }
    }

注意GetEnumerator方法,它并不会返回IEnumerator<T>,相反,它遍历data数组中的各项,并依次返回每一项.重点在于yield关键字的使用.yiled关键词每次迭代要返回的值。
可这样理解yield语句:yield本意是放弃或让路,后因为一些语义的变化,有了生成、生产的意思。而yield return关键字表达的意思是,暂时让出控制权,返回(生成的)值.它临时将方法暂停,将一个值传回调用者。当调用者需要下一个值时,GetEnumerator方法就从上次暂停的地方继续,生成下一个值。

GetEnumerator定义了一个迭代器,编译器利用这些代码实现IEnumerator<T>接口,其中包含Current属性和MoveNext方法。
在这里插入图片描述
在这里插入图片描述
我们可以通过提供附加属性来实现IEnumerable接口,并用一个迭代器返回数据,实现按相反顺序输出。
在这里插入图片描述

使用迭代器为Tree<TItem>类定义枚举器

之前我们定义了TreeEnumerator<TItem> 来实现枚举器
现在我们可以使用迭代器来实现

        IEnumerator<TItem> IEnumerable<TItem>.GetEnumerator()
        {
            if(this.LeftTree != null)
            {
                foreach(TItem item in this.LeftTree)
                {
                    yield return item;
                }
            }
            yield return this.NodeData;
            if(this.RightTree != null)
            {
                foreach(TItem item in this.RightTree)
                {
                    yield return item;
                }
            }
        }

在这里插入图片描述
可以看到运行正常.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值