我們先去看看公開的.Net4.0的源程序中IEnumerable<T>,、IEnumerable,、IEnumerator<T>和IEnumerator這四個(gè)接口是如何聲明的: 需加微信交流群的,,請加小編微信號(hào)zls20210502,,切記備注 加群,,小編將會(huì)第一時(shí)間邀請你進(jìn)群,!目前一群滿員,,只能進(jìn)二群了! public interface IEnumerable<out T> : IEnumerable { new IEnumerator<T> GetEnumerator(); }
public interface IEnumerator<out T> : IDisposable, IEnumerator { new T Current { get; } }
public interface IEnumerable { IEnumerator GetEnumerator(); }
public interface IEnumerator { bool MoveNext(); Object Current { get; } void Reset(); } 一,、接口IEnumerable實(shí)現(xiàn) 1,、建一個(gè)學(xué)生數(shù)據(jù)結(jié)構(gòu)和一個(gè)學(xué)生集合類: //student數(shù)據(jù)結(jié)構(gòu) class Student { public int id; public string name; }
//student 集合 class StudentCollection { public List<Student> students = new List<Student>(); public void Add(Student student) { students.Add(student); } }
公開一個(gè)Add()方法以添加數(shù)據(jù),我們的集合類建立完畢,。下來添加數(shù)據(jù): static void Main(string[] args) { StudentCollection sc = new StudentCollection(); sc.Add(new Student { id=0,name="Tony"}); sc.Add(new Student { id=1,name="Micheal"}); sc.Add(new Student { id =2, name = "Amy" }); foreach(var s in sc) {...} } }
當(dāng)我們想用foreach()遍歷的時(shí)候,,編譯器會(huì)告訴我們StudentCollection不包含GetEnumerator,不能用foreach遍歷,。雖然StudentCollection里面有能用遍歷的List<T>,,但我們不想在屬性上迭代,,我們想在類上迭代,不能 foreach(var s in sc.students){...} 現(xiàn)在只有把我們的StudentCollection類改造成能foreach的,。 2,、繼承接口IEnumerable: 當(dāng)我們在類后面加上:IEnumerable后,Visual Studio IDE會(huì)冒出來一個(gè)小黃燈泡,,點(diǎn)進(jìn)去有提示自動(dòng)填充接口的約定,,我們選第一項(xiàng)實(shí)現(xiàn)接口(Visaul Studio是全世界最貼心的IDE!),IDE會(huì)幫我們把SudentCollection改造成以下的: class StudentCollection:IEnumerable { public List<Student> students = new List<Student>(); public void Add(Student student) { students.Add(student); }
public IEnumerator GetEnumerator() { throw new NotImplementedException(); } }
加了一個(gè)返回迭代器的方法GetEnumrator,。下來按照IEnumetator接口的約定來實(shí)現(xiàn)我們的迭代器StudentCollectionEnumerator,,用IDE自動(dòng)補(bǔ)全代碼如下: //迭代器 class StudentCollectionEnumerator : IEnumerator { public object Current { get { throw new NotImplementedException(); } }
public bool MoveNext() { throw new NotImplementedException(); }
public void Reset() { throw new NotImplementedException(); } }
我的理解是:Current返回當(dāng)前元素,MoveNext移動(dòng)到下一個(gè),,Reset回到第一個(gè)元素,。但根據(jù)MSDN上面的說法,Reset 方法提供的 COM 互操作性,。它不一定需要實(shí)現(xiàn),;相反,實(shí)施者只需拋出NotSupportedException,。但是,,如果您選擇執(zhí)行此操作,則應(yīng)確保沒有調(diào)用方依賴于Reset功能,。 迭代器工作的原理是:先調(diào)用MoveNext()方法,,然后讀取Current得到元素,直到MoveNext返回false,。 我們需要3個(gè)字段分別放置 元素的位置,、元素、元素集,。改變后的程序如下: //迭代器 class StudentCollectionEnumerator : IEnumerator { private int _index; private List<Student> _collection; private Student value; public StudentCollectionEnumerator(List<Student> colletion) { _collection = colletion; _index = -1; } object IEnumerator.Current { get { return value; } } public bool MoveNext() { _index++; if (_index >= _collection.Count) { return false; } else { value = _collection[_index]; } return true; } public void Reset() { _index = -1; }
}
首先,,迭代器初始化,引入元素集 _collection,,并把索引 _index設(shè)置成-1,。設(shè)置成-1而不是0是因?yàn)榈魇紫日{(diào)用MoveNext,在MoveNext里面我們先把索引+1指向下一個(gè)元素,,如果索引_index的值初始為0,,則第一個(gè)元素是元素集[1],第二個(gè)元素了,。 其次,,我們要把object Current改成 IEnumerator.Current,這個(gè)是實(shí)現(xiàn)迭代器的關(guān)鍵。返回元素,。(好像有裝箱的行為) 第三,,在MoveNext方法內(nèi)累加索引,并從元素集中讀取元素,。然后讓索引值超出元素集返回個(gè)false值,。 最后,在Reset方法內(nèi)讓索引值為-1,,不過好像直接拋出錯(cuò)誤也成。 迭代器寫好了,,我們在StudentColletion類里面調(diào)用: class StudentCollection : IEnumerable { public List students; public StudentCollection() { students = new List(); } public void Add(Student student) { students.Add(student); } public IEnumerator GetEnumerator() { return new StudentCollectionEnumerator(students); } }
測試運(yùn)行一下,,大功告成!我們實(shí)現(xiàn)了可枚舉的自己的類,。 通過觀察,,發(fā)現(xiàn)迭代器主要就是返回一個(gè)元素對象,而StudentColletion里面的students元素集是List的,,本身就能枚舉,,我們能不能利用這個(gè)不用專門寫迭代器來實(shí)現(xiàn)枚舉呢? 答案是肯定的,我們這樣寫: class StudentCollection:IEnumerable { public List<Student> students = new List<Student>(); public void Add(Student student) { students.Add(student); }
public IEnumerator GetEnumerator() { foreach(var s in students) { yield return s; } } }
這樣就能實(shí)現(xiàn)枚舉了,,真簡單,,充分利用了.Net給出的各種可枚舉集合,不用再去寫GetEnumerator這種累活了,。 二,、接口IEnumerable<T>實(shí)現(xiàn) 如果我們想寫一個(gè)通用的可foreach的類,用到泛型: class MyCollection<T> { public List<T> mycollection = new List<T>(); public void Add(T value) { mycollection.Add(value); } }
其實(shí)這個(gè)MyCollection類只不過是在List<T>外面封裝了一層,,要實(shí)現(xiàn)IEnumable<T>,,繼承該泛型接口,Visual Studio 的IDE自動(dòng)幫我們補(bǔ)全后,,如下: class MyCollection:IEnumerable { public List mycollection = new List(); public void Add(T value) { mycollection.Add(value); } public IEnumerator GetEnumerator() { throw new NotImplementedException(); } IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); } }
我們直接用上面第二個(gè)簡單的寫法,,改成: class MyCollection:IEnumerable { public List mycollection = new List(); public void Add(T value) { mycollection.Add(value); } public IEnumerator GetEnumerator() { foreach(var s in mycollection) { yield return s; } } IEnumerator IEnumerable.GetEnumerator() { foreach (var s in mycollection) { yield return s; } } }
測試運(yùn)行: static void Main(string[] args) { MyCollection mc = new MyCollection(); mc.Add(0); mc.Add(1); mc.Add(2); foreach(var s in mc) { Console.WriteLine(s); } Console.ReadKey(); }
大功告成! 雖然第二種寫法比較投機(jī),,充分利用了.NET Framework給的各種泛型集合可枚舉的特征,。不過我們也自己實(shí)現(xiàn)了一個(gè)GetEnumerator(),了解了枚舉器的工作原理,。本章學(xué)習(xí)目的達(dá)成,。
|