前几天,一位网友跟我说他编写的一段很简单的代码遇到了奇怪的Bug,他要达到的效果是从一个List中随机取出来一条数据,代码如下:

1 var random = new Random();
2 var users = Enumerable.Range(0, 10).Select(p => new User(p, "A" + p)).ToList();
3 var user = users.Find(p => p.Id == random.Next(0, 10));
4 Debug.Assert(user != null);
5
6 record User(int Id,string Name);

第2行代码生成了一个包含10个User对象的List,这些User的Id值从0递增到9;第3行代码中调用List的Find方法来根据lambda表达式来查找一条数据,这里通过random.Next()来获取一个[0,10)之间的随机数,然后用这个随机数来和Id进行比较。按照逻辑来讲,Find一定可以找到一条数据,所以在第4行代码中断言user一定不为null。但是这段代码有的时候运行正常,有的时候则会断言失败,从而程序抛出异常,令人不解。

当然,他的这段代码写的过于复杂,其实改成users[random.Next(0, 10)]就简单又高效。但是为了揭示问题的本质,我这里继续分析为什么用Find+lambda方法会出现问题。

我们查看一下Find方法的源代码,如下:

public T? Find(Predicate<T> match)
{
for (int i = 0; i < _size; i++)
{
if (match(_items[i]))//注意这里
{
return _items[i];
}
}
return default;
}

Find方法的逻辑很简单,就是遍历List中的数据,对于每条数据都调用match这个委托来判断当前这条数据是否满足条件,如果找到一条满足条件的数据,就把它返回。如果走到最后都没有找到,就返回默认值(比如null)。这个逻辑简单到貌似看不到任何问题。

问题的关键就在if (match(_items[i]))这一句代码。它是在每一次循环都调用一下match的委托来判断当前数据的匹配性。而match指向的委托的方法体是p => p.Id == random.Next(0, 10),也就是每次匹配判断都要获取一个新的随机数来进行比较。假设在循环的时候生成的10个随机数为:9,8,8,7,9,1,1,2,3,4,那么就会每次match(_items[i])判断的结果都为false,从而导致最后返回null,也就是找不到任何的数据。

明白了原理之后,解决这个问题的思路就是不要在lambda中生成待比较的随机数,而是提前生成随机数,代码如下:

int randId = random.Next(0, 10);
var user = users.Find(p => p.Id == randId);

同样的原理也适用于Single()、Where()等LINQ操作。在这些操作中也要避免在lambda表达式中再进行复杂的计算,这样不仅可以避免类似这篇文章中提到的bug,而且可以提升程序的运行效率。

欢迎阅读我编写的《ASP.NET Core技术内幕与项目实战》,这本书的宗旨就是“讲微软文档中没有的内容,讲原理、讲实践、讲架构”。具体见右边公告。

天坑,这样一个lambda随机取数据也有Bug的更多相关文章

  1. MySQL随机取数据

    // 随机取9个 $rand_sql = "SELECT * FROM `tf_product` WHERE (`id` >= ((SELECT MAX(`id`) FROM `tf_ ...

  2. MySQL 随机取数据效率问题

    本文详细解说了MySQL Order By Rand()效率优化的方案,并给出了优化的思路过程,是篇不可多得的MySQL Order By Rand()效率美文. 最近由于需要大概研究了一下MYSQL ...

  3. SQL 在表中随机取数据

    在一张10万行产品表(Product)中,随机取10条数据的几种方式: SET STATISTICS IO ON SELECT TOP 10 ID FROM dbo.Product(NOLOCK) W ...

  4. mysql实现高效率随机取数据

    从数据库中(mysql)随机获取几条数据很简单,但是如果一个表的数据基数很大,比如一千万,从一千万中随机产生10条数据,那就相当慢了,如果同时一百个人访问网站,处理这些个进程,对于一般的服务器来说,肯 ...

  5. Sql Server随机取数据

    select top 10 * from tablename order by NEWID()

  6. oracle随机取数据

    select * from (select rownum,KEYWORD, CATEGORY,CREATE_DATE,UPDATE_DATE from (select * from knet_keyw ...

  7. mysql 随机取数据

    SELECT * FROM table WHERE id >= (SELECT FLOOR(RAND()*MAX(id)) FROM table ) ORDER BY idLIMIT 1; 这样 ...

  8. MySQL随机获取数据的方法,支持大数据量

    最近做项目,需要做一个从mysql数据库中随机取几条数据出来. 总所周知,order by rand 会死人的..因为本人对大数据量方面的只是了解的很少,无解,去找百度老师..搜索结果千篇一律.特发到 ...

  9. 【MySQL】随机获取数据的方法,支持大数据量

    在mysql中带了随机取数据的函数,在mysql中我们会有rand()函数,很多朋友都会直接使用,如果几百条数据肯定没事,如果几万或百万时你会发现,直接使用是错误的.下面我来介绍随机取数据一些优化方法 ...

  10. Oracle的trunc和dbms_random.value随机取n条数据

    今天在review项目代码的时候看到这样一个问题,有一张号码表,每次需要从这样表中随机取6个空闲的号码,也就是每次取出来的6个号码应该都会有所不同.然后我就看到了这样的SQL select   t.* ...

随机推荐

  1. CentOS 7配置Chrony服务进行时间同步

    CentOS 7版本中使用Chrony工具实现本地时间与标准时间同步.与CentOS 6版本中的NTP服务不同,Chrony可以更快更准确地同步系统时钟,最大程度的减少时间和频率误差.Chrony包含 ...

  2. 使用logstash同步mysql 多表数据到ElasticSearch实践

    参考样式即可,具体使用配置参数根据实际情况而定 input { jdbc { jdbc_connection_string => "jdbc:mysql://localhost/数据库 ...

  3. k8s控制器理解

    DaemonSet 一个DaemonSet对象能确保其创建的Pod在集群中的每一台(或指定)Node上都运行一个副本.如果集群中动态加入了新的Node,DaemonSet中的Pod也会被添加在新加入N ...

  4. 《Vision Permutator: A Permutable MLP-Like ArchItecture For Visual Recognition》论文笔记

    论文题目:<Vision Permutator: A Permutable MLP-Like ArchItecture For Visual Recognition> 论文作者:Qibin ...

  5. Oracle基础知识汇总一

    Oracle基础知识 以下内容为本人的学习笔记,如需要转载,请声明原文链接   https://www.cnblogs.com/lyh1024/p/16720759.html oracle工具: SQ ...

  6. 使用python制作动图

    利用python制作gif图 引言 当写文章时候,多张图片会影响排版,可以考虑制作gif图 准备 pip install imageio 代码 # This is a sample Python sc ...

  7. CEOI2020 道路(Roads) Solution

    直接来构造. 考虑扫描线.从左到右扫,考虑当前扫到了一个左端点,我们把这个左端点连到其他点上. 我们可以找到这个点下方离他最近的线段,并且记下每条线段上方在扫描线左侧且最靠右,与这条线段中间没有其他线 ...

  8. bean文档类型定义

    ELEMENT:表示当前 (bean*):表示0到多个bean元素 (property*):表示0到多个property元素 ATTLIST:表示属性 #REQUIRED:表示不可缺少 #IMPLIE ...

  9. python 网络爬虫全流程教学,从入门到实战(requests+bs4+存储文件)

    python 网络爬虫全流程教学,从入门到实战(requests+bs4+存储文件) requests是一个Python第三方库,用于向URL地址发起请求 bs4 全名 BeautifulSoup4, ...

  10. JUC(7)四大函数式接口

    文章目录 1.四大函数式接口(必须掌握) 1.1 function 1.2 Predicate 1.3 Consumer 1.4 Supplier 1.四大函数式接口(必须掌握) 1.lambda表达 ...