前天在項目中遇到一個問題,foreach遍歷過程當中修改responses中的對象,其中responses的類型:IEnumerable<Order>,代碼以下:html
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是一個實現式接口,它定義了具體的實現方法。下面首先介紹其定義:post
定義IEnumerator接口:this
public interface IEnumerator { object Current{ get; } bool MoveNext(); void Reset(); }
定義IEnumerable接口:url
public interface IEnumerable { IEnumerator GetEnumerator(); }
瞭解定義後,下面實例具體實現IEnumerable和IEnumerator:spa
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); } }
進行調用:3d
string[] strList = {"1", "2", "4", "8"}; MyIEnumerable my = new MyIEnumerable(strList); var tt = my.GetEnumerator(); while (tt.MoveNext()) { Console.Write("{0} ", tt.Current); }
結果以下:code
那麼在項目中,如何自定義一個類實現foreach,繼承IEnumerable便可,以下實例:htm
定義實體類:
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結合使用,表示迭代結束。
引用: