c#通过linq方式加载xml_就是这么坑:Linq的延迟加载特性

b47b7d83a89c90086b42be3514e69b1b.png

Linq拥有简洁的形式、强大的表达,是C#初学者必须了解的基础,是.Net开发者组织代码的利器。

学习Linq的时候,几乎所有的书都会介绍,绝大部分的Linq语法具有延迟加载的特性。例如:

IEnumerable

当使用Linq的Where方法操作数组集合时,方法并没有返回最终结果,即int[]{2,3},而是返回一个接口类型IEnumerable<int>。只有当foreach的时候,才会逐个返回结果中的元素。为什么呢?因为类似Where的Linq操作,不是在内存里重新开辟内存存放结果,而是只保存了查询的命令,这样我们可以在后面继续增加新的命令形成一系列的组合操作,不至于每次查询都浪费资源生成新的中间结果。

然而,也有少数的Linq命令是即时加载的,比如Count、First、Last等方法。因为这些方法本身就涉及到了遍历各个元素,既然内部需要foreach遍历,自然就不是延迟加载了。

好了,简要地介绍了一下Linq的延迟加载特性,让我们来假设一个简单的应用情形:

某地举办了一场演唱会,每张门票都有id,和购票的粉丝绑定。演唱会嘛,就是去听粉丝一起唱唱歌,顺便也看看偶像的现场表演。这不,演唱会过程中有一个粉丝和声活动,由最后一个幸运粉丝嗷个结尾。

粉丝定义如下:

public 

粉丝和声的过程代码描述如下:

int

好了,现在让我们看一下Sing(IEnumerable<Fan>)方法怎么实现。很多人可能都会这么写:

private 

可以看到方法内使用了Linq的Last方法来获取最后一个粉丝;然后遍历粉丝,开始回声迭起的联欢;到了最后一位粉丝时,由这位幸运粉丝嗷最后一句。

让我们运行一下看看:

b629d6d9e2c04da9104e374e1a9d3303.png
一个失败的结尾

其他人:哎?最后这位粉丝你怎么回事?让你唱结尾,不是让你唱开头哇!

最后一位粉丝:???(很无辜)

出问题了,问题在哪儿呢?

直接原因是“theLastFan == fan”的判断失效了,而Fan是引用类型。

哦,此fan非彼fan呀!

在LastOrDefault()方法执行时,因为传递的是IEnumerable对象,方法会在内部逐个遍历找到最后一位粉丝,这里触发了Select内部的操作,产生了一个Fan的对象。

而下面接着开始foreach操作,其实又是逐个执行了Select命令!好了,最后一位粉丝与之前的并不是同一个对象,尽管他们的Id都是3——真假美猴王啊……(说到美猴王……直接开花)

所以——真相只有一个!3号票的粉丝上厕所去了,有吃瓜群众冒领了Ta的位置,但是又没有了解规则,所以跟着大家唱了开头的一句!保安!保安!(额,我想一定是我最近推理小说看多了……)

那么怎么改呢?

一种方式是让粉丝集合先加载再传入好了:

IEnumerable

这样为什么可以呢?这里又涉及到性能细节了。虽然Linq方法的传入参数是接口类型IEnumerable<T>,但是如果确认了传入参数的具体类型,有助于提升性能。比如Count()方法,如果我们知道它其实是ICollection类型,那么直接访问Count属性就可以了,对于绝大部分继承该接口的集合来说,这是一个时间复杂度为O(1)的操作(自己实现的其实也应该保证);如果不能确定呢?那只能一个个地加了,复杂度就是O(n)。

同样地,贴一下LastOrDefault()方法的源代码,加深一下读者的理解:

public 

所以,传入前加载好,传入的对象就是一个数组(当然也可以用ToList方法生成列表),这样Last方法获取的就是数组内已经确定的对象。

考虑到实际项目往往都是多人开发,你总不能要求所有开发成员在使用Sing方法时,都要牢记传入Linq结果前保证加载吧?在这方面,机器比人靠谱得多,我更推荐增强代码的健壮性来防呆。

所以另一种方式是将Sing方法实现改为:

private 

我们在方法内部主动判断一次传入参数是否已经加载,如果没有,则先加载生成的结果,再执行遍历结果。

让我们看看现在的执行结果:

06180c571137856ed06f35adf48395f4.png
完美的尾声

好了,演唱会互动环节顺利地结束了,粉丝们发出了兴奋的欢呼声。

参考网址:

LINQ 查询简介 (C#)​docs.microsoft.com
3c4e4f7742db840bdb1b0f377a23ced7.png

后记

转眼已经2019年了,距离我上一次更新专栏已经过去了8个多月。这期间经历了很多,好几次想写写文章,但都被忙和懒打败了。直到最近同事踩了个坑,感觉挺有意思,介绍起来也不甚麻烦,就抽出一点时间写了这篇文章。

另外,在国内对.Net生态普遍悲观的现状下,能保持学习C#热情的人,是很可贵的(当然,在另一些人的眼中,也可能是愚蠢,but who cares?)。而在未更新的8个多月里,专栏居然还逐渐涨到了接近200人的关注,虽然不多,但我很感谢关注的你们。我完全有动力将专栏一直更新下去,尽管频率极不稳定。

.Net Core近年来的发展势头喜人,但是历史告诉我们:靠微软的施舍是不行的。要舍身投入开源社区、积极贡献开源力量,才是正道!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值