我们先思考几个问题:
1.为什么在foreach中不能修改item的值?(IEnumerator的Current为只读)
2.要实现foreach需要满足什么条件?(实现IEnumerator接口来实现的)
3.为什么Linq to Object中要返回IEnumerable?(因为IEnumerable是延迟加载的,每次访问的时候才取值。也就是我们在Lambda里面写的where、select并没有循环遍历(只是在组装条件),只有在ToList或foreache的时候才真正去集合取值了。这样大大提高了性能。)
.net中迭代器是通过IEnumerable和IEnumerator接口来实现的,今天我们也来依葫芦画瓢。
using System; using System.Collections; namespace RedisTest { class Program { static void Main(string[] args) { string[] str = { "1111", "2222", "3333", "4444", "5555" }; var aaa = new MyIEnumerable(str); var bbb = aaa.GetEnumerator(); while (bbb.MoveNext()) { Console.WriteLine(bbb.Current); } Console.WriteLine("---------------------------------------------------------"); foreach (var item in aaa) { Console.WriteLine(item); } Console.Read(); /* 1111 2222 3333 4444 5555 --------------------------------------------------------- 1111 2222 3333 4444 5555 */ } } public class MyIEnumerable : IEnumerable { private string[] strList; public MyIEnumerable(string[] _strList) { strList = _strList; } public IEnumerator GetEnumerator() { return new MyIEnumerator(strList); } } public class MyIEnumerator : IEnumerator { private string[] strList; private int position; public MyIEnumerator(string[] _strList) { 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; } } }
yield的使用
using System; using System.Collections; namespace RedisTest { class Program { static void Main(string[] args) { string[] str = { "1111", "2222", "3333", "4444", "5555" }; var aaa = new MyIEnumerable(str); var bbb = aaa.GetEnumerator(); while (bbb.MoveNext()) { Console.WriteLine(bbb.Current); } Console.WriteLine("---------------------------------------------------------"); foreach (var item in aaa) { Console.WriteLine(item); } Console.Read(); /* 1111 2222 3333 4444 5555 --------------------------------------------------------- 1111 2222 3333 4444 5555 */ } } public class MyIEnumerable { private string[] strList; public MyIEnumerable(string[] _strList) { strList = _strList; } public IEnumerator GetEnumerator() { for (int i = 0; i < strList.Length; i++) { yield return strList[i]; } } } }
我们调用GetEnumerator的时候,看似里面for循环了一次,其实这个时候没有做任何操作。只有调用MoveNext的时候才会对应调用for循环:
using System; using System.Collections; using System.Collections.Generic; using System.Linq; namespace RedisTest { class Program { static void Main(string[] args) { string[] str = { "1111", "2222", "3333", "4444", "5555" }; var aaa = new MyIEnumerable(str); var bbb = aaa.MyWhere(x => x != "1111"); var ccc = bbb.ToList(); //现在看到了吧。执行到MyWhere的时候什么动作都没有(返回的就是IEnumerable),只有执行到ToList的时候才代码才真正的去遍历筛选。 //这里的MyWhere其实可以用扩展方法来实现,提升逼格。(Linq的那些查询操作符就是以扩展的形式实现的) Console.Read(); } } public class MyIEnumerable { private string[] strList; public MyIEnumerable(string[] _strList) { strList = _strList; } public IEnumerator GetEnumerator() { for (int i = 0; i < strList.Length; i++) { yield return strList[i]; } } public IEnumerable<string> MyWhere(Func<string, bool> func) { foreach (string item in this) { if (func(item)) { yield return item; } } } } }
时间: 2024-10-05 13:26:29