IEnumerable和IEnumerator詳解

引言

IEnumerable是可枚舉的所有非泛型集合的基接口, IEnumerable包含一個方法GetEnumerator(),該方法返回一個IEnumerator;IEnumerator提供通過Current屬性以及MoveNext()和Reset()方法來循環訪問集合的功能。

IEnumerable 接口

公開枚舉數,該枚舉數支持在非泛型集合上進行簡單迭代。接口源碼如下:

public interface IEnumerable
{
    [DispId(-4), __DynamicallyInvokable]
    IEnumerator GetEnumerator();
}

IEnumerator 接口

支持對非泛型集合的簡單迭代。接口源碼如下:

public interface IEnumerator
{
    [__DynamicallyInvokable]
    bool MoveNext();
    [__DynamicallyInvokable]
    object Current { [__DynamicallyInvokable] get; }
    [__DynamicallyInvokable]
    void Reset();
}

舉例說明

示例演示了通過實現IEnumerable和IEnumerator接口來循環訪問自定義集合的最佳實踐。

定義一個簡單的實體類:

public class Person
    {
        public Person(string name, int age)
        {
            this.Name = name;
            this.Age = age;
        }
        public string Name;
        public int Age;
    }

定義一個實體類的集合,繼承IEnumerate:

 public class People : IEnumerable
    {
        private Person[] _people;
        public People(Person[] pArray)
        {
            _people = new Person[pArray.Length];
            for (int i = 0; i < pArray.Length; i++)
            {
                _people[i] = pArray[i];
            }
        }
        /// <summary>
        /// GetEnumerator方法的實現
        /// </summary>
        /// <returns></returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
        public PeopleEnum GetEnumerator()
        {
            return new PeopleEnum(_people);
        }
    }

定義一個枚舉器,繼承IEnumerator:

public class PeopleEnum : IEnumerator
    {
        public Person[] _people;

        /// <summary>
        /// 枚舉器位於第一個元素之前直到第一個MoveNext()調用。
        /// </summary>
        private int position = -1;
        public PeopleEnum(Person[] list)
        {
            _people = list;
        }

        public bool MoveNext()
        {
            position++;
            return position < _people.Length;
        }

        public void Reset()
        {
            position = -1;
        }

        object IEnumerator.Current => Current;

        public Person Current
        {
            get
            {
                try
                {
                    return _people[position];
                }
                catch (IndexOutOfRangeException)
                {
                    throw new InvalidOperationException();
                }
            }
        }
    }

具體調用:

 Person[] peopleArray = new Person[3]
            {
                new Person("張三", 15),
                new Person("李四", 18),
                new Person("王五", 21),
            };
 People peopleList = new People(peopleArray);
            
 foreach (Person p in peopleList)
          Console.WriteLine(p.Name + "\t" + p.Age);

輸出:

其中,上邊調用中foreach等價於

IEnumerator enumeratorSimple = peopleList.GetEnumerator();
while (enumeratorSimple.MoveNext())
   {
        Person p = enumeratorSimple.Current as Person;
        Console.WriteLine(p?.Name + "\t" + p?.Age);
   }

通過例子,可以得出:

  • 實現一個自定義集合,繼承於IEnumerate,必須實現一個枚舉器;
  • C# 語言的foreach語句隱藏了枚舉數的複雜性,因此, 建議foreach使用, 而不是直接操作枚舉器;
  • 枚舉器可用於讀取集合中的數據,但不能用於修改基礎集合。

總結

IEnumerable代表繼承此接口的類(比如ArrayList,IList,List<T>等)可以獲取一個IEnumerator來實現枚舉這個類中包含的集合中的元素的功能,是 .NET Framework 中最基本的集合訪問器。在編程中,Lambda表達式通過Select()或者Where()返回的變量爲IEnumerate<T>,此時我們可以通過foreach遍歷。希望本文對你有所幫助,下一篇介紹Lambda中的Select和Where,感興趣的朋友可以加關注,歡迎留言交流!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章