IEnumerable 真是基础中的基础,然而.....
我们直接来看看这个接口的实现吧;
它是一个公开枚举数,该枚举数支持在非泛型集合上进行简单的迭代。换句话说,对于所有数组的遍历,都来自IEnumerable,那么我们就可以利用这个特性,来定义一个能够遍历xxxxxx的通用方法
先看我们的经典实例1:
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication4 { public class Person:IEnumerable { public string name; public int age; public Person(string _name, int _age) { name = _name; age = _age; } private Person[] per; public Person(Person[] arr) { per=new Person[arr.Length]; for (int i = 0; i < arr.Length; i++) { per[i] = arr[i]; } } //实现这个接口; public IEnumerator GetEnumerator() { return new PersonEnum(per); } } class PersonEnum : IEnumerator//实现foreach语句内部,并派生 { public Person[] _per; //实现数组; int position = -1; public PersonEnum(Person[] list) { _per = list; } public bool MoveNext() { position++; return (position < _per.Length); } public void Reset() { position = -1; } public object Current //实现接口的方法; { get { try { return _per[position]; } catch (IndexOutOfRangeException) { throw new InvalidOperationException();//抛出异常信息 } } } } class Program { static void Main(string[] args) { Person[] per = new Person[2] { new Person("guojing",21), new Person("muqing",21), }; //这个。我们就可以进行枚举了; Person personlist = new Person(per); foreach (Person p in personlist) //本质上是对数组的枚举,还是通过我们的额index 来实现的滴呀;,只不过要支持foreach的写法,自然要实现我们的ienumberable的重写; { Console.WriteLine("Name is " + p.name + " and Age is " + p.age); //本质上是一种写法的改变滴呀; } var obj=new Person("muqing", 21); foreach (var p in obj) { //Console.WriteLine("Name is " + p.name + " and Age is " + p.age); bug } Console.ReadLine(); } } }
再看我们经典实例二:
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication5 { public class charlist : IEnumerable { private string TargetStr { get; set; } public charlist(string str) { this.TargetStr = str; } public IEnumerator GetEnumerator() { return new charIterator(TargetStr); } } public class charIterator:IEnumerator { public string TargetString { get; set; } public int position { get; set; } public charIterator(string str) { this.TargetString = str; this.position = this.TargetString.Length; } public object Current { get { if (this.position == -1 || this.position == this.TargetString.Length) { throw new InvalidOperationException(); } return this.TargetString[this.position]; } } public bool MoveNext() { if (this.position != -1) { this.position--; } return this.position > -1; } public void Reset() { this.position = this.TargetString.Length; } } class Program { static void Main(string[] args) { //我这里是倒着枚举出来滴哎呦; string val = "fuck the life"; charlist list = new charlist(val); foreach (var o in list) { Console.WriteLine(o); } Console.ReadLine(); } } }
这里顺便提一下我们的 yield return 用法的复习和使用滴呀;
上面的两种实现方式可以说是我们net 1.0 的各种常见做法滴呀;
后面,我们将尝试yield return的用法;
for (int index = this.TargetStr.Length; index > 0;index-- )
{
yield return this.TargetStr[index - 1];
}
关于我们的yield return的使用;
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication6 { class Program { /// <summary> /// 获取数据 /// </summary> /// <returns></returns> static List<int> GetData() { return new List<int>() { 1, 2, 3, 4 }; } static IEnumerable<int> WithoutYield() { List<int> result = new List<int>(); foreach (var i in GetData()) { if (i > 2) { result.Add(i); } } return result; } static IEnumerable<int> WithYield() { foreach (var i in GetData()) { if (i > 2) { yield return i; } } } static void Main(string[] args) { Console.WriteLine("不使用我们的额yield return的结果;"); foreach(var val in WithoutYield()) { Console.WriteLine(val); } Console.WriteLine("使用我们的额yield return的结果;"); foreach (var val in WithYield()) { Console.WriteLine(val); } //如果使用我们的f11 调试你会发现,yield 是取出一个就马上返回一个,效果是非常好滴呀; //而第一个的做法,是得到所有的结果之后在一次性返回; Console.ReadLine(); } } }
通过调试发现:两种方法的输出结果是一样的,但实际的运作过程是不同的。
第一种方法,是把结果集全部加载到内存中再遍历;
第二种方法,遍历每调用一次,yield return就返回一个值;
因此,当希望获取一个IEnumerable<T>类型的集合,而不想把数据一次性加载到内存,就可以考虑使用yield return的方式去实现;
这里还得说说使用它的一个坑滴呀;
yield 语句只能出现在 iterator 块中,这种块可作为方法、运算符或访问器的主体实现。 这类方法、运算符或访问器的体受以下约束的控制:
- 不允许不安全块。
- 方法、运算符或访问器的参数不能是 ref 或 out。
- yield return 语句不能放在 try-catch 块中的任何位置。 该语句可放在后跟 finally 块的 try 块中。
- yield break 语句可放在 try 块或 catch 块中,但不能放在 finally 块中。
重点是,不能放在我们的try catch 语句中滴呀;
时间: 2024-10-11 07:36:48