foreach 的本质
实现foreach循环先满足IEnumerable接口IEnumerator 是所有非泛型枚举数的基接口。
IEnumerator 接口 支持对非泛型集合的简单迭代。
C# 语言的 foreach 语句(在 Visual Basic 中为 for each)隐藏了枚举数的复杂性。
枚举数可用于读取集合中的数据,但不能用于修改基础集合。
最初,枚举数定位在集合中第一个元素前。Reset 方法还会将枚举数返回到此位置。在此位置,调用 Current 属性会引发异常。因此,在读取 Current 的值之前,必须调用 MoveNext 方法将枚举数提前到集合的第一个元素。
在调用 MoveNext 或 Reset 之前,Current 返回同一对象。MoveNext 将 Current 设置为下一个元素。
如果 MoveNext 越过集合的末尾,则枚举数将被放置在此集合中最后一个元素的后面,而且 MoveNext 返回 false。当枚举数位于此位置时,对 MoveNext 的后续调用也返回 false。如果最后一次调用 MoveNext 返回 false,则调用 Current 会引发异常。若要再次将 Current 设置为集合的第一个元素,可以调用 Reset,然后再调用 MoveNext。
只要集合保持不变,枚举数就保持有效。如果对集合进行了更改(如添加、修改或删除元素),则枚举数将失效且不可恢复,并且下一次对 MoveNext 或 Reset 的调用将引发 InvalidOperationException。如果在 MoveNext 和 Current 之间修改集合,那么即使枚举数已经无效,Current 也将返回它所设置成的元素。
枚举数没有对集合的独占访问权;因此,枚举通过集合在本质上不是一个线程安全的过程。即使一个集合已进行同步,其他线程仍可以修改该集合,这将导致枚举数引发异常。若要在枚举过程中保证线程安全,可以在整个枚举过程中锁定集合,或者捕捉由于其他线程进行的更改而引发的异常。
namespace ConsoleApplication1 { public class Person { public string firstName; public string lastName; public Person(string fName, string lName) { this.firstName = fName; this.lastName = lName; } } 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]; } } public IEnumerator GetEnumerator() { return new PeopleEnum(_people); } } public class PeopleEnum : IEnumerator { public Person[] _people; // Enumerators are positioned before the first element // until the first MoveNext() call. int position = -1; public PeopleEnum(Person[] list) { _people = list; } public bool MoveNext() { position++; return (position < _people.Length); } public void Reset() { position = -1; } public object Current { get { try { return _people[position]; } catch (IndexOutOfRangeException) { throw new InvalidOperationException(); } } } } public class DogEnum : IEnumerator { #region IEnumerator 成员 public object Current { get { throw new NotImplementedException(); } } public bool MoveNext() { throw new NotImplementedException(); } public void Reset() { throw new NotImplementedException(); } #endregion } class Program { static void Main(string[] args) { Person[] peopleArray = new Person[3] { new Person("John", "Smith"), new Person("Jim", "Johnson"), new Person("Sue", "Rabon") }; People peopleList = new People(peopleArray); //foreach foreach (Person p in peopleList) { Console.WriteLine(p.firstName + " " + p.lastName); } //foreach本质上是这样调用的 IEnumerator myrator = peopleList.GetEnumerator(); myrator.Reset(); while (myrator.MoveNext()) { Person p = (Person)myrator.Current; Console.WriteLine(p.firstName + " " + p.lastName); } } } }
返回迭代器yield
class App { class Product { public string ProductName { get; set; } public decimal? ProductPrice //decimal? 可空类型null { get; set; } } static void Main() { Product[] ps = { new Product{ ProductName="iPone6", ProductPrice=3000}, new Product{ProductName="iPone7", ProductPrice=3400}, new Product{ProductName="iPone8", ProductPrice=5500}, new Product{ProductName="iPoneX", ProductPrice=8000}, }; //用yield的写法 var psList = GetCheaperPhone(ps); //用Linq的写法 //var psList = from po in ps // where po.ProductPrice <= 3500 // select po; //输出满足条件的集合 foreach (var p in psList) { Console.WriteLine(p.ProductName + ":" + p.ProductPrice); } } //普通List写法 //private static IEnumerable<Product> GetCheaperPhone(Product[] ps) //{ // List<Product> pianyihuo = new List<Product>();//先申明一个装商品的集合 // for (int i = 0; i < ps.Length; i++) // { // if (ps[i].ProductPrice <= 3500) // { // pianyihuo.Add(ps[i]); // } // } // return pianyihuo; //} private static IEnumerable<Product> GetCheaperPhone(Product[] ps) { for (int i = 0; i < ps.Length; i++) { if (ps[i].ProductPrice <= 3500) { yield return ps[i]; } } } }