【橙子老哥】C# 关于Linq Sum求和的冷知识

hello,大家好久不见,欢迎来到橙子老哥的分享时间,希望大家一起学习,一起进步。

欢迎加入.net意社区,第一时间了解我们的动态,文章第一时间分享至社区

社区官方地址:https://ccnetcore.com (上千.neter聚集地)(当前注册人数:2400)

官方微信公众号:公众号搜索 意.Net 

官方微信小程序:小程序搜索 意.Net

添加橙子老哥微信加入官方微信群:chengzilaoge520

团队官网/产品矩阵:https://SharpDance.cn

1、概述

废话少说,今天来给大家讲讲一个冷知识,非常非常冷,冷到都几乎以为是微软写的bug,当然,微软这么严谨的c#,这么做,肯定有他的理由

Sum,就是一个经常用于求和的linq方法,使用频率也是非常高,特别是要求对金额等一些数字进行计算用的多

先给大家卖个关子,提出几个关于Sum的问题,请问:

  1. [0,null,0],Sum求和是多少

  2. [null,null,null],Sum求和是多少

var result = new List<decimal?>() { 0, null, 0 }.Sum();
var result2 = new List<decimal?>() { null, null, null }.Sum();

同时,我们再看看具体Sum是返回什么的:

public static decimal? Sum([NotNull, InstantHandle] this IEnumerable<decimal?> source)
,位置:class System.Linq.Enumerable)

返回 decimal? 哦,可空类型

大家可以凭借着自己的经验去思考下

2、解答

其实很多小伙伴已经猜到了,无论传入是0还是null,返回的都是0

这样其实很简单,我们看一下他的源码实现:

public static Decimal? Sum(this IEnumerable<Decimal?> source) => source.Sum<Decimal, Decimal>();

privatestatic TSource? Sum<TSource, TAccumulator>(this IEnumerable<TSource?> source)
    where TSource : struct, INumber<TSource>
    where TAccumulator : struct, INumber<TAccumulator>
  {
    if (source == null)
      ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
    TAccumulator zero = TAccumulator.Zero;
    foreach (TSource? nullable in source)
    {
      if (nullable.HasValue)
        checked { zero += TAccumulator.CreateChecked<TSource>(nullable.GetValueOrDefault()); }
    }
    returnnew TSource?(TSource.CreateTruncating<TAccumulator>(zero));
  }

这段源码是.net8的,如果大家翻过.net6的源码,就很简单了,微软没有对类型进行封装,而是每个struct都复制一堆重复的代码,这段被封装后的方法,我们分析下流程:

  1. 初始化数据:TAccumulator zero = TAccumulator.Zero;

  2. 循环累加:zero += TAccumulator.CreateChecked<TSource>

  3. new一个Decimal,赋值TSource.CreateTruncating<TAccumulator>(zero)其实TSource.CreateTruncating中,是为了判断是否类型相等,如果类型是一样的,直接返回

换句话来说,他在初始化数据的时候,就定了数据类型,TAccumulator.Zero这个是一个抽象的,我们看一下Decimal的实现

static Decimal INumberBase<Decimal>.Zero => Decimal.Zero;

嗯,没错,sum的第一件事,其实就是创建了默认值0,所以他,永远也不会返回null

所以问题就来了,作为一门如此严谨的编程语言,为什么微软要把一个永远不会返回null的方法,返回一个可空类型呢?而且这样的设计还不少,不单单是Decimal,微软这么做肯定是有原因的

大家可以再思考一下

3、解答

原因之一:与Sql语义兼容 总所皆知,linq既可以使用在内存中,也可以使用在EF中,翻译成sql

数据源类型

场景

返回值类型

返回值可能为null吗?

List in memory

空集合/全是 null

Decimal?

永远返回 0

EF Core 查询

SQL 中全是 NULL

Decimal?

可能返回 null

很明显,微软想到了这一点,为了兼容Sql的这种数据类型,统一Api,真可谓是用心良苦

4、深思

比起怎么解决这个问题,其实我们更应该反思,这个问题为什么会出现,我们应该注意什么

我给大家一个实际场景

比如你要统计一列数据的合,但是这些数据可能是空行,压根就没有,如果你直接使用sum进行求和,返回了个0,结果实际返回与预期想返回null不符合,相当于被sum吃了null的状态

5、End

最后,献上我们团队的官网:https://SharpDance.cn构建智能未来的开发者社区 专注于.NET技术、人工智能与互联网前沿技术的专业开发者社区。 我们致力于推动技术创新,连接优秀开发者,共同探索智能时代的无限可能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值