多角度让你彻底明白yield语法糖的用法和原理及在C#函数式编程中的作用
如果大家读过dapper源码,你会发现这内部有很多方法都用到了yield关键词,那yield到底是用来干嘛的,能不能拿掉,拿掉与不拿掉有多大的差别,首先上一段dapper中精简后的query方法,先让大家眼见为实。
private static ienumerable<t> queryimpl<t>(this idbconnection cnn, commanddefinition command, type effectivetype) { object param = command.parameters; var identity = new identity(command.commandtext, command.commandtype, cnn, effectivetype, param?.gettype()); var info = getcacheinfo(identity, param, command.addtocache); idbcommand cmd = null; idatareader reader = null; bool wasclosed = cnn.state == connectionstate.closed; try { while (reader.read()) { object val = func(reader); if (val == null || val is t) { yield return (t)val; } else { yield return (t)convert.changetype(val, converttotype, cultureinfo.invariantculture); } } } }
一:yield探究
1. 骨架代码猜想
骨架代码其实很简单,方法的返回值是ienumerable
有自己的猜想还不行,还得相信权威,看msdn的解释: 如果你在语句中使用 yield 上下文关键字,则意味着它在其中出现的方法、运算符或 get 访问器是迭代器。 通过使用 yield 定义迭代器,可在实现自定义集合类型的 ienumerator
没用过yield之前,看这句话肯定是一头雾水,只有在业务开发中踩过坑,才能体会到yield所带来的快感。 为了方便探究原理,我来写一个不能再简单的例子。 对,就是这么简单,接下来用ilspy反编译打开这其中的神秘面纱。 从截图中看最让人好奇的有两点。 好奇心驱使着我看一下这个类到底都有些什么?由于il代码太多,我做一下精简,从下面的il代码中可以发现,果然是实现了ienumerable接口,如果你了解设计模式中的迭代器模式,那这里的movenext,current是不是非常熟悉?
2. msdn解释
3. 从il入手
public static void main(string[] args)
{
var list = getlist(new int[] { 1, 2, 3, 4, 5 });
}
public static ienumerable<int> getlist(int[] nums)
{
foreach (var num in nums)
{
yield return num;
}
}
<1> 无缘无故的多了一个叫做<getlist>d__1 类