zoukankan      html  css  js  c++  java
  • C# 通过IEnumberable接口和IEnumerator接口实现泛型和非泛型自定义集合类型foreach功能

    IEnumerator和IEnumerable的作用

    其实IEnumerator和IEnumerable的作用很简单,就是让除数组和集合之外的类型也能支持foreach循环,至于foreach循环,如果不清楚,请参考C# foreach循环较for循环的优势与劣势

    代码如下:

    static void Main(string[] args)
    {
           CatList cats = new CatList();
           foreach (var cat in cats)
           {
                Console.WriteLine(cat.NickName);
           }
    }
    
    public class CatList
    {
           public string NickName { get; set; }
           public int Age { get; set; }
    }

    以上代码说明自定义集合类型(假设CatList是集合类型)是无法使用foreach进行循环的.

    原因是C#中自定义集合类型要实现foreach的功能,必须通过IEnumerator和IEnumerable两个接口来实现!

    1、通过IEnumerator和IEnumerable两个接口实现自定义集合类型的foreach循环功能.    非泛型版本

    第一步:实现自定义集合类型实现IEnumerable接口,实现该接口的字面意思可以理解为:自定义集合类型实现了该接口,就拥有了"可枚举的功能".代码如下:

    实现了IEnumerable接口之后,发现该接口规定必须返回一个IEnumerator接口(迭代器对象).ok,那么就必须返回一个IEnumerator,那么它是什么呢?别急,下面开始介绍这个接口!

    第二步:通过IEnumerable要求实现的GetEnumerator()方法返回一个IEnumerator(迭代器对象),实现该接口必须实现以下三个方法/属性:

    (1)、MoveNext()   ---将当前遍历的枚举数推进到集合的下一个.

    注:如果 MoveNext 越过集合的末尾,则枚举数将被放置在此集合中最后一个元素的后面,而且 MoveNext 返回 false。当枚举数位于此位置时,对MoveNext 的后续调用也返回 false。如果最后一次调用 MoveNext 返回 false,则调用 Current 会引发异常。若要再次将 Current 设置为集合的第一个元素,可以调用 Reset,然后再调用 MoveNext

    (2)、Current属性   ---返回正在遍历的集合中的元素,注:该属性没有set方法,所以在循环遍历的时候不能设置值.

    (3)、Reset()   ---重置当前正在遍历的集合中元素的索引.

    第三步:具体实现

    在介绍完上面两个接口之后,开始具体的实现,现在需要编写一个People类,该类是一个Person集合,要求People类拥有foreach循环的功能,代码如下:

    public class People : IEnumerable
        {
            private Person[] persons;
    
            public People(Person[] persons)
            {
                Persons = persons;
            }
    
            public Person[] Persons { get => persons; set => persons = value; }
    
            public IEnumerator GetEnumerator()
            {
                return new PersonEnum(Persons);
            }
    
            public class PersonEnum : IEnumerator
            {
                private Person[] perons;
                private int _index=-1;
    
                public PersonEnum(Person[] persons)
                {
                    Perons = persons;
                }
    
                public object Current => Perons[_index];
    
                public Person[] Perons { get => perons; set => perons = value; }
    
                public bool MoveNext()
                {
                    _index++;
                    if (_index < perons.Length)
                        return true;
                    else
                        return false;
                }
    
                public void Reset()
                {
                    _index = 0;
                }
            }
        }

    第四步:验证代码,代码如下:

    Person[] persons ={
             new Person(){FirstName="Stephen",LastName="Curry"},
             new Person(){FirstName="Lebron",LastName="James"},
             new Person(){FirstName="Kobe",LastName="Brant"}
    };
    People people = new People(persons);
    foreach (var p in people)
    {
         Console.WriteLine(((Person)p).LastName);
    }

     

    第五步:分析原理

    总结分析下上面的代码,实现foreach代码的基本原理如下:

    1、编写自定义集合类,实现IEnumerable接口,通过GetEnumerator()方法返回一个迭代器对象实例.

    2、通过自定义集合类的构造函数,或者方法,或者属性(都可以),初始化一个类数组   !Important

    3、将初始化完的类数组作为参数传递给迭代器类

    4、编写迭代器类,create 构造函数,接收自定义集合类初始化完的类数组

    5、实现IEnumerator(迭代器)接口,实现对应的三个方法,通过编写三个方法发现,其实迭代器就是简单的对数组进行的操作

    第六步:执行自定义集合的循环

    执行方式有两种:

    (1)、foreach

    Person[] persons ={
          new Person(){FirstName="Stephen",LastName="Curry"},
          new Person(){FirstName="Lebron",LastName="James"},
          new Person(){FirstName="Kobe",LastName="Brant"}
    };
    
    People people = new People(persons);
    foreach (Person p in persons)
    {
            Console.WriteLine(p.LastName);
    }
    Console.ReadKey();

    (2)、通过自定义集合类的GetEnumerator()方法

    Person[] persons ={
             new Person(){FirstName="Stephen",LastName="Curry"},
             new Person(){FirstName="Lebron",LastName="James"},
             new Person(){FirstName="Kobe",LastName="Brant"}
    };
    
    People people = new People(persons);
    IEnumerator er = people.GetEnumerator();
    while (er.MoveNext())
    {
       Console.WriteLine(((Person)er.Current).LastName);
    }

    分析两种不同的调用方式,foreach语句可以理解为是第二种方式的语法糖.

    2、通过IEnumerable<T>和IEnumerator<T>实现自定义集合类,并实现简单的添加功能

        class Program
        {
            static void Main(string[] args)
            {
                var list = new CustomList<int>(2);
                list.Add(1);
                list.Add(2);
                list.Add(2);
    
                var p = new int[] { 1, 2, 2 };
                var list1 = new CustomList<int>(p);
                Console.WriteLine(list.Count+"..."+list1.Count);
                Console.ReadKey();
            }
        }
    
        public class CustomList<T> : IEnumerable<T>
        {
            private T[] _ts;
    
            private int _index = 0;
    
            public CustomList(T[] ts)
            {
                _ts = ts;
                _index = ts.Length;
            }
    
            public CustomList(int capcity)
            {
                _ts = new T[capcity];
            }
    
            public int Count => _index;
    
            public void Add(T t)
            {
                if (_index >= _ts.Length)
                {
                    //如果调用Add方法导致数组的长度大于我们给定的长度
                    //则创建一个新的数组,并将其长度扩大为原来的两倍
                    T[] newArr = new T[_ts.Length * 2];
    
                    //将原来数组中的数据拷贝到新的数组中
                    Array.Copy(_ts, newArr, _ts.Length);
    
                    //使_ts指向新的数组
                    _ts = newArr;
                }
                _ts[_index++] = t;
            }
    
            public IEnumerator<T> GetEnumerator()
            {
                return new CustomEnumerator<T>(_ts);
            }
    
            IEnumerator IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }
        }
    
        public class CustomEnumerator<T> : IEnumerator<T>
        {
            private T[] _ts;
    
            private int _position = -1;
    
            public CustomEnumerator(T[] ts)
            {
                _ts = ts;
            }
    
            public T Current => _ts[_position];
    
            object IEnumerator.Current => this.Current;
    
            public bool MoveNext()
            {
                _position++;
                if (_position < _ts.Length)
                    return true;
                return false;
            }
    
            public void Reset()
            {
                _position = 0;
            }
    
            public void Dispose()
            {
                throw new NotImplementedException();
            }
        }

  • 相关阅读:
    如何根据select选择的值反查option的属性
    如何让数据库的某张表在记录达到100条的时候自动删除记录
    一个程序员如何发表演讲或者答辩?
    适合程序员演讲的定场诗
    字符串与二进制之间的相互转化
    百鸡百钱问题
    如何把he_llo wo_rld 变成 HeLlo WoRld
    SpringCloud(三)Eureka注册中心实现高可用
    SpringCloud(二)注册服务提供者搭建
    SpringCloud (一)Eureka注册中心搭建
  • 原文地址:https://www.cnblogs.com/GreenLeaves/p/7419541.html
Copyright © 2011-2022 走看看