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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM