• 天坑,这样一个lambda随机取数据也有Bug


    前几天,一位网友跟我说他编写的一段很简单的代码遇到了奇怪的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 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技术内幕与项目实战》,这本书的宗旨就是“讲微软文档中没有的内容,讲原理、讲实践、讲架构”。具体见右边公告。

  • 相关阅读:
    音视频项目—基于FFmpeg和SDL的音视频播放器解析(十八)
    nodejs+vue电影在线预定与管理系统的设计与实现-微信小程序-安卓-python-PHP-计算机毕业设计
    SQL39道代码练习题
    操作系统复习第三章:处理机调度与死锁
    “现在的自动驾驶太保守!”看看轻舟智航联合创始人大方这篇论文怎么说
    前端构建流程与包管理工具npm与yarn的区别
    浙大MBA二战上岸:笔试备考辛酸路
    多项采购订单一起发票校验生成的会计凭证供应商行项目可根据订单分开吗
    永州出入境检验实验室建设那些事
    MFC Windows 程序设计[150]之上报列表图标全集
  • 原文地址:https://www.cnblogs.com/rupeng/p/16961982.html