假如有一个集合,里面有数字1-10,现在想实现从这10个数字中取出偶数数字,然后将得到的偶数乘以10,最后输出前三个。代码如下:
代码非常的简单,最后会得到20、40、60这三个数字,但结果并不是我们今天最关注的事。今天我们想讨论下numbers中的数字是如何Where、Select、Take这三个方法中流转的。也许你会认为,是这样流转的。如下:
一般我们会向上图这样理解:Where中生成并返回符合条件的新数组,然后将这个数组交个Select处理,Select中生成并返回新数组,再交由Take处理,Take取出前三个数并返回新数组。
其实并不是这样,我们知道Linq是由一系列基于IEnumerable的扩展方法组成,返回值也都是IEnumerable,而IEnumerable只是一个迭代器对象,每次读取IEnumerable对象时,其实只是遍历里面的一个元素。上面方法的真正流程应该是流式的类似管道的操作,即读一个数字处理一个,边读边处理。如下:
这样数据逐个在方法中处理就构成了数据管道,这里还有一个地方需要注意,很多Linq方法是延迟操作,比如我们例子中的Where、Select、Take这些,但ToList、Count、Sum会立即执行。延迟操作就是只是定义如何操作数据,但不会真正执行,等到数据真正使用的时候才会执行。我们来验证下,比如我在Select中故意抛一个异常,试下。代码如下:
我们再次执行一下,异常不会报出。代码如下:
其实代码中的filter变量可以理解成一堆算法的包装器,只是封装了一系列对数据的操作,但只有元素被使用时才会执行。我们在代码后加一段遍历filter并输出的代码,这时元素被使用到了,就会报错了。代码如下:
好了,我们要讲的内容就讲完了,内容非常浅,相信很多人都是知道的,大家可以去看下Linq的源码,相信收获会更多。管道的思想其实应用非常多,比如链式编程、建造者模式,在dotcore中用的就更多了。
高冷地说声拜拜~~