Foreach遍歷


前天在項目中遇到一個問題,foreach遍歷過程中修改responses中的對象,其中responses的類型:IEnumerable<Order>,代碼如下:

foreach (Order item in responses)
            {
                if (string.IsNullOrEmpty(item.Creator))
                    item.Creator = item.Creator2;
            }

結果可想而知,response的對象並沒有被改變。這是為什么?

 

弄清楚問題之前需要明白什么是foreach。foreach語句為數組或者對象集合的每一個元素重復一個嵌入語句組,foreach語句用於循環訪問集合以獲取所需信息,但不應更改集合信息以避免不可預知的負作用。(百度百科)Foreach可以循環訪問集合以獲取所需信息,為什么foreach不能更改集合信息,什么樣的對象能夠foreach?這就涉及到IEnumerable和IEnumerator.

 

IEnumerable和IEnumerator是兩個用來實現枚舉的接口,相互協助來完成一個具有枚舉功能能使用foreach的集合。IEnumerable是一個聲明性接口,一個集合對象要foreach,必須實現IEnumerable接口,也既是必須以某種方式返回IEnumerator object。IEnumerator是一個實現式接口,它定義了具體的實現方法。下面首先介紹其定義:

定義IEnumerator接口:

public interface IEnumerator
    {
        object Current{ get; }

        bool MoveNext();
        void Reset();
    }

 定義IEnumerable接口:

public interface IEnumerable
    {
        IEnumerator GetEnumerator();
    }

 

了解定義后,下面實例具體實現IEnumerable和IEnumerator:

public class MyIEnumerator : IEnumerator
    {
        private string[] strList;
        private int position;

        public MyIEnumerator(string[] strList)
        {
            this.strList = strList;
            position = -1;
        }

        public object Current
        {
            get { return strList[position]; }
        }

        public bool MoveNext()
        {
            position ++;
            if (position < strList.Length) return true;
            return false;
        }

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

 

public class MyIEnumerable : IEnumerable
    {
        private string[] strList;

        public MyIEnumerable(string[] strList)
        {
            this.strList = strList;
        }
        public IEnumerator GetEnumerator()
        {
            return new MyIEnumerator(strList);
        }
    }

 

進行調用:

string[] strList = {"1", "2", "4", "8"};
            MyIEnumerable my = new MyIEnumerable(strList);

            var tt = my.GetEnumerator();
            while (tt.MoveNext())
            {
                Console.Write("{0} ", tt.Current);
            }

 

結果如下:

 

 

那么在項目中,如何自定義一個類實現foreach,繼承IEnumerable即可,如下實例:

定義實體類:

public class Student
    {
        public int Id;
        public string Name;

        public Student(int id, string name)
        {
            Id = id;
            Name = name;
        }
    }

 

定義student的集合類School,繼承IEnumerable集合:

public class School : IEnumerable
    {
        public Student[] stu = new Student[3];

        public School()
        {
            Create();
        }

        public void Create()
        {
            stu[0] = new Student(1, "tom");
            stu[1] = new Student(2, "john");
            stu[2] = new Student(3, "mali");
        }

        public IEnumerator GetEnumerator()
        {
            return this.stu.GetEnumerator();
        }
    }

 

使用foreach遍歷school:

School school = new School();

            foreach (Student item in school.stu)
            {
                Console.WriteLine("Id is {0} ,Name is {1}", item.Id, item.Name);
            }

            foreach (Student item in school)
            {
                Console.WriteLine("Id is {0} ,Name is {1}", item.Id, item.Name);
            }

 

結果如下:

 

此時一個概念“迭代器”應該進入大家的視野。

 迭代器是一種對象,它能夠用來遍歷標准模板庫容器的部分或者全部元素,每個迭代器對象代表容器中的確定地址。

迭代器使開發人員能夠在類或者結構中支持foreach迭代,而不必整個實現IEnumerable和IEnumerator接口。只需要提供一個迭代器,即可遍歷類中的數據結構。當編譯器檢測到迭代器時,將自動生成IEnumerable和IEnumerator接口中的相應方法。

 

另外一個概念yield:yield關鍵字向編譯器指示它所在的方法是迭代器。編譯器生成一個類來實現迭代器塊中表示的行為。在迭代器塊中,yield關鍵字與return關鍵字結合使用,向枚舉器對象提供值。yield關鍵字也可與break結合使用,表示迭代結束。

 

引用:

IEnumerable 使用foreach 詳解

IEnumerator和IEnumerable的關系

 

先說IEnumerable,我們每天用的foreach你真的懂它嗎?

 


免責聲明!

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



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