C#:iif 非短路计算及“尝试除以 0”异常

此文章发布于 11 个月前,部分信息可能已经过时,请自行斟酌确认。

问题描述1:iif 不短路

如下调试所示,DataTable.Compute() 方法中使用 iif 函数,条件为 ture,期望得到的结果是 0,而不需要计算后面的 1.00/0
现象表明虽然条件为 true,但后面的 false 的结果表达式仍被计算了,于是发生异常。

表达式:

dt.Compute("iif(0=0,0,1.00/0))"),"")

20211220_170158.png

原因:

C# 中的 iif 与三元表达式 true?a:b 不一样,iif 为非短路,就算条件为 true 仍会计算 false 的表达式。
分析其源码得知内部是封装了方法,而 a 和 b 是做为参数传入的,所以传入时会先计算两个值再运行 iif 判断,所以报错。

源码调试

20211219_205002.png

20211219_205009.png

20211219_205023.png

问题描述2:尝试除以 0 (DivideByZeroException)

如上所述 0.00/0 会发生异常
0/0 不会发生异常(注意:这里不发生异常是写在 DataTable.Compute() 中),如下图:

20211220_170340.png

原因分析:

参考 MSDN 官方文档:
https://docs.microsoft.com/zh-cn/dotnet/api/system.dividebyzeroexception#remarks
https://stackoverflow.com/questions/48929855/why-doesnt-dividing-by-zero-with-doubles-throw-an-exception

其中明确说明:

尝试将整数或数字除以 Decimal 零会引发 DivideByZeroException 异常。将浮点值除以零不会引发异常;根据 IEEE 754 算法的规则,它会导致正无穷、负无穷或非 (NaN) 的数字。

所以和计算的数据类型有关,Double 是可以正常运算的,结果如下代码所示:

示例代码:

Console.WriteLine(1 / 0); //编译error: Division by constant zero

int i = 0;
Console.WriteLine(1 / i); //throws: DivideByZeroException

double d = 0;
Console.WriteLine(0 / d);  //ok: NaN (不是数字) 
Console.WriteLine(1 / d);  //ok: Infinity (正无穷大) ∞
Console.WriteLine(-1 / d); //ok: -Infinity (负无穷大) -∞ Negative Infinity 

DataTable.Compute() 中字符串表达式 0/0 会被解析为 Double 类型,所以不会异常。但 0.0/0 会解析为 Decimal 因而异常
Console.WriteLine(dt.Compute("0/0",""));   //正常输出:NaN
Console.WriteLine(dt.Compute("0.0/0","")); //发生异常:DivideByZeroException

解决方案:

如果想在 DataTable.Compute() 中实现需求又不报错,可以变通方式如下:

//以[数量=金额/单价] sl=je/dj 为例
decimal dj = 0.00M;
decimal je = 100.01M;
Console.WriteLine(dt.Compute($"{je}/{dj}", ""));                //throws: DivideByZeroException
Console.WriteLine(dt.Compute($"iif({dj}=0,0,{je}/{dj})", ""));  //throws: DivideByZeroException
Console.WriteLine(dt.Compute($"{(dj == 0 ? 0 : je)}/{(dj == 0 ? 0 : dj)}", ""));  //ok:NaN
Console.WriteLine(dt.Compute($"{(dj == 0 ? 0 : je)}/{(dj == 0 ? 1 : dj)}", ""));  //ok:0,巧妙的将表达式转变为 0/1,得到0,真是妙

参考文档

https://stackoverflow.com/questions/3586975/what-is-iif-in-c
https://stackoverflow.com/questions/822810/iif-equivalent-in-c-sharp
https://stackoverflow.com/questions/10746871/datacolumn-expression-divide-by-zero
https://stackoverflow.com/questions/50565276/c-sharp-datatable-divide-by-0-makes-8
https://stackoverflow.com/questions/4609698/inconsistency-in-divide-by-zero-behavior-between-different-value-types

最后修改:2021 年 12 月 20 日 05 : 05 PM
如果觉得我的文章对你有用,请随意赞赏

发表评论