此文章发布于35
个月前,部分信息可能已经过时
,请自行斟酌确认。
问题描述1:iif 不短路
如下调试所示,DataTable.Compute()
方法中使用 iif
函数,条件为 ture,期望得到的结果是 0
,而不需要计算后面的 1.00/0
。
现象表明虽然条件为 true,但后面的 false 的结果表达式仍被计算了,于是发生异常。
表达式:
dt.Compute("iif(0=0,0,1.00/0))"),"")
原因:
C# 中的 iif 与三元表达式 true?a:b
不一样,iif 为非短路,就算条件为 true 仍会计算 false 的表达式。
分析其源码得知内部是封装了方法,而 a 和 b 是做为参数传入的,所以传入时会先计算两个值再运行 iif 判断,所以报错。
源码调试
问题描述2:尝试除以 0 (DivideByZeroException)
如上所述 0.00/0
会发生异常
而 0/0
不会发生异常(注意:这里不发生异常是写在 DataTable.Compute()
中),如下图:
原因分析:
参考 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