例子:在不使用yieId时,通常我们都会采取先遍历再把元素加到新的List中
using (var reader = SqlHelper.ExecuteReader("")) { if (reader.HasRows) { IList<Captcha> list = new List<Captcha>(); while (reader.Read()) { list.Add(SqlHelper.MapEntity<Captcha>(reader)); } return list; } } return null;
接下来我们用它(yieId)来改善以上代码:
using (var reader = SqlHelper.ExecuteReader("")) { if (reader.HasRows) { while (reader.Read()) { yield return SqlHelper.MapEntity<Captcha>(reader); } } }
这里需要注意的是在使用yieId时,返回值类型必须为IEnumerable
现在我们简单介绍下yieId,先分享一个错误的理解,注意 是错误的理解
根据上图,我们定义一个变量作用域然后开辟一个List变量,在代码执行的过程中都通过这个List来Add这个元素。最后返回这个新的List。这可以当成我们对yieId的初步理解,但yieId绝对不是那么简单的。
我们来写一个简单的Demo,让您更快的理解yieId。
如题:定义一个string数组,然后将这个数组转换成Int数组
string[] strs = new string[] { "1", "2", "3" };
比较普遍的处理方式就是遍历这些元素添加到一个新的数组里,至于代码怎么实现这里就不讲了。今天的重点是如何通过yieId来更高效的实现这个效果
首先我们来定一个一个静态类并且返回值类型为IEnumerable的方法:
static IEnumerable<int> GetInts(string[] strs) { foreach (var item in strs) { yield return Convert.ToInt32(item); } }
当然,这里用linq来实现将回更简洁,不过这不是咱们今天的话题,就不聊它了。
以上代码就如上图所说,先开辟一个临时空间,再通过yieId return把数据丢到这个临时空间中。最后把临时空间的数据全部放进去
string[] strs = new string[] { "1", "2", "3" }; int[] ints = GetInts(strs).ToArray(); foreach (var item in ints) { Console.WriteLine(item.GetType()); } Console.ReadKey();
为了让大伙能看的更明白,我这边使用GetType(),以便有效的让您知道读出来的数据类型是否准确
这样,移掉GetType()就能直接得到1,2,3 的结果了
到这边,我想您应该对yieId有一个自己的初步理解了吧?接下来我们来分析下yieId带给我们的好处。
1.代码更精简 (这个刚才您应该有体会到了)
除此之外还有一个特点,我们来改动之前的这一段代码:
static void Main(string[] args) { string[] strs = new string[] { "1", "2", "3" }; int[] ints = GetInts(strs).ToArray(); foreach (var item in ints) { Console.WriteLine(item.GetType()); } Console.ReadKey(); } static IEnumerable<int> GetInts(string[] strs) { foreach (var item in strs) { Console.WriteLine(item.GetType());//改动1 yield return Convert.ToInt32(item); } }
请思考这个执行出来的结果会是怎么样的呢?(先自动屏蔽下图哦)
这个结果还是合情合理的,在调用GetInts()时,数组的类型还是String,并且调用3次。再执行遍历,转换成Int类型,结果刚好是3个string 3个int。
那为什么会是3个string 3个int了,原因是因为我们调用了.ToArray()这个方法,我们尝试把这个方法去掉。但这时我们需要改变被转换的变量的返回值类型
IEnumerable<int> ints = GetInts(strs);
执行结果:
对于这个执行结果,就开始有点好奇了。怎么会是这样呢?我们来研究下yieId到底发挥了什么效果。
根据上图,在调试的过程中我们发现,在执行F11(逐语句)时,并不会跑到GetInts()这个方法体中来。那就说白了,当我们在使用yieId return的时候,它并没有真正的被执行。
当我们执行foreach遍历的时候,这个ints的变量事先会去调用GetEnumerator()这个方法来拿到这个迭代器,然后通过这个迭代器去迭代的时候,就像去读这个数组的时候遍历最后才调用这个GetInts(),
就像一个指针去搜寻每一个元素一样。另外,在调试中,当yieId return后,yieId会将这个方法体进行冻结。当需要继续执行的时,会根据遍历的次数来继续执行。这时指针会继续往下移动一位。
这时,我们回到之前用List去实现这样的效果,从代码执行上来看。就会浪费了很多不需要的循环。在使用yieId后,我们可以有效的去处理这个代码执行效率的问题,我们在遍历这个ints时,加一层判断
if (item > 1) break;
这样当不满足条件时,就不会去使用它(GetInts())这个方法,相当于它自动被冻结住了。我们看一下执行结果:
这就是yieId的好处,能很好的提高代码执行效率。有很多人几乎都没接触甚至根本不知道这个东西的存在,其实个人认为只是因为我们所见到的市场代码质量不是很高。其实只要你去看一些比较著名的开源项目,比如EF,那里面都会大量使用到这个yieId关键词,包括MVC的源码里也很多。