visual C#(十九)枚举集合

参考书:《 visual C# 从入门到精通》
第三部分 用C#定义可扩展类型
第19章 枚举集合

19.1 枚举集合中的元素

我们前面用foreach语句来方便的遍历集合,实际上foreach语句只能遍历可枚举集合,就是实现了System.Collections.IEnumerable接口的集合。IEnumerable接口中包含一个名为GetEnumerator的方法,返回实现该接口的枚举器对象。枚举器对象用于遍历集合中的元素。IEnumerator接口指定了一下属性和方法:

object Current{get;}
bool MoveNext();
void Reset();

将枚举器看成指针,最开始指向第一个元素之前的位置,然后调用MoveNext方法,指针成功移动到下一项则返回true,否则返回false。用Current访问当前指向的那一项,用Reset方法使指针返回到第一项的前一项位置。这样用集合的GetEnmerator方法创建枚举器,然后反复调用MoveNext,获取Current的值,这个过程正是foreach语句 做的事情。当然类库也提供了泛型IEnumerable<T>接口。

19.1.1 手动实现枚举器

在VS中新建一个控制台应用程序,添加一个源文件为第17章的二叉树类Tree.cs,然后我们创建一个TreeEnumerator.cs

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;

namespace c_19_1_1
{
    class TreeEnumerator<TItem>: IEnumerator<TItem> where TItem:IComparable<TItem>
    {
        private Tree<TItem> currentData = null;
        private TItem currentItem = default(TItem);
        private Queue<TItem> enumData = null;

        TItem IEnumerator<TItem>.Current
        {
            get
            {
                if (this.enumData == null)
                    throw new InvalidOperationException("Use MoveNext before calling Current");
                return this.currentItem;
            }
        }

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

        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);
        }

        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();
        }

        #region IDisposable Support
        private bool disposedValue = false; // 要检测冗余调用

        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    // TODO: 释放托管状态(托管对象)。
                }

                // TODO: 释放未托管的资源(未托管的对象)并在以下内容中替代终结器。
                // TODO: 将大型字段设置为 null。

                disposedValue = true;
            }
        }

        // TODO: 仅当以上 Dispose(bool disposing) 拥有用于释放未托管资源的代码时才替代终结器。
        // ~TreeEnumerator()
        // {
        //   // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
        //   Dispose(false);
        // }

        // 添加此代码以正确实现可处置模式。
        void IDisposable.Dispose()
        {
            // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
            //Dispose(true);
            // TODO: 如果在以上内容中替代了终结器,则取消注释以下行。
            // GC.SuppressFinalize(this);
        }
        #endregion
    }
}

生成解决方案,成功就说明程序没有明显的语法错误。

19.1.2 实现IEnumerable接口

将源文件Tree.cs中的代码稍作修改:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;

namespace c_19_1_1
{
    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)
        {
            this.NodeData = nodeValue;
            this.LeftTree = null;
            this.RightTree = null;
        }
        public void Insert(TItem newItem)
        {
            TItem currentNodeValuie = this.NodeData;
            if (currentNodeValuie.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();
        }
    }
}

Program.cs中测试枚举器:

using System;

namespace c_19_1_1
{
    class Program
    {
        static void Main(string[] args)
        {
            Tree<int> tree1 = new Tree<int>(10);
            tree1.Insert(5);
            tree1.Insert(11);
            tree1.Insert(5);
            tree1.Insert(-12);
            tree1.Insert(15);
            tree1.Insert(0);
            tree1.Insert(14);
            tree1.Insert(-8);
            tree1.Insert(10);
            foreach(int item in tree1)
            {
                Console.WriteLine(item);
            }
        }
    }
}

运行结果:

-12
-8
0
5
5
10
10
11
14
15

C:\Users\xhh\Source\Repos\c_19_1_1\c_19_1_1\bin\Debug\netcoreapp3.1\c_19_1_1.exe (进程 26196)已退出,代码为 0。
按任意键关闭此窗口. . .

19.2 用迭代器实现枚举器

迭代器是能生成已排序值序列的一个代码块。

19.2.1 一个简单的迭代器

class BasicCollection<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 daturm;
        }
    }
    IEnumerator IEnumerable.GetEnumerator(){
        throw new notImplementedException();
    }
}

yield的使用很关键,它指定每一次迭代要返回的值。GetEnumerator方法中的代码定义了一个迭代器。编译器利用这些代码实现IEnumerator<T>接口,其中包括CurrentMoveNext。可以采取和平常一样的方式调用迭代器生成的枚举器:

BasicCollection<string>bc=new BasicCollection<string>();
bc.FillList("Twas","bring","and","the","slithy","toves");
foreach(string word in bc){
    Console.WriteLine)(word);
}

还可以通过附加属性实现按相反顺序获取数据:

class BasicCollection<T>:IEnumerable<T>{
    ...;
    public IEnumerable<T>Reverse{
        get{
            for(int i=data.Count-1;i>=0;--i){
                yield return data[i];
            }
        }
    }
}

调用该属性:

BasicCollection<string> bc=new BasicCollection<string>();
bc.FillList("Twas","bring","and","the","the","slithy","toves");
foreach(string word in bc.Reverse)
    Console.WriteLine(word);

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

回到我们前面的程序,将Tree.cs中的IEnumerable<TItem>.GetEnumerator方法改为:

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;
        }

然后其它的都不变,运行一下发现能正常运行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值