C#如何调试dt.Rows.Add方法内的算术溢出错误
问题背景
开发中实现的AddTrailer方法运行时抛出**Arithmetic operation resulted in an overflow(算术运算导致溢出)**错误,涉及的DataTable包含192526行、9列数据。
核心问题点:
- 需要找到在
dt.Rows.Add()每个入参位置设置断点的方法,准确定位出错对应的行、列信息 - 之前尝试执行到该行代码时使用「StepInto(单步步入)」调试,在
dt.AsEnumerable().Sum(dr => dr.Field<int>("Balance"))步骤看到error CS0103: The name 'dt' does not exist in the current context错误,判断这不是运行失败根因,但入参项数量多,逐次StepOver调试效率极低 - 尝试将求和代码修改为
dt.AsEnumerable().Sum(dr => (long)dr.Field<int>("Balance"))后,抛出新错误:Value was either too large or too small for an Int32.Couldn't store <21488943305> in Balance|Current Balance Column. Expected type is Int32.,暂时无法定位根因,判断当前数据表行数、单行列值都远未超出int类型取值范围。
涉及业务代码如下:
public DataTable AddTrailer(DataTable dt) { try { if (dt.TableName.Equals("Summary")) { Console.WriteLine("Summary Table"); dt.Rows.Add( null, null, null, null, null, null, dt.AsEnumerable().Sum(dr => dr.Field<int>("Balance")), dt.AsEnumerable().Sum(dr => dr.Field<int>("Total1")), dt.AsEnumerable().Sum(dr => dr.Field<int>("Total2")) ); } } catch (Exception ex) { Console.WriteLine("Exception thrown in the AddTrailer - Summary"); throw; } return dt; }
解决方案
1. 高效断点调试方法
不要直接在dt.Rows.Add()整行打断点单步,按以下方式操作可精准定位每个入参的计算问题:
- 将所有入参的计算逻辑拆为单独临时变量,不要直接写在
Add()方法参数列表里。C#执行多参数方法时会按从左到右顺序逐个计算参数值,把参数拆为独立变量后,可直接在每个变量赋值行打断点,计算到哪一步抛异常就能直接定位到对应列。 - 如果不想修改原有代码逻辑,可使用Visual Studio即时窗口分步计算每个参数:当程序执行停在
dt.Rows.Add()所在行时,打开即时窗口逐行输入每个入参的计算表达式执行,哪一步抛异常就是对应位置的问题。之前调试时出现CS0103错误,是因为单步进入LINQ lambda表达式时,当前执行上下文不在方法主栈帧,切回AddTrailer方法的栈帧即可正常访问dt变量。
2. 溢出错误根因说明
两个报错本质是同一个问题:
- 最初的算术溢出错误,是因为
Sum方法默认用int类型做累加计算。单行列值、行数确实没超出int范围,但19万行数据累加后的总和为21488943305,远大于int类型最大值2147483647,累加过程中数值超过int上限就会抛出溢出错误。 - 改成
long类型累加后,求和结果是正确的long值,但DataTable里的Balance、Total1、Total2列类型定义为Int32,无法存储long类型的汇总值,因此会抛出值超出Int32范围的类型不匹配错误。
3. 修复代码参考
public DataTable AddTrailer(DataTable dt) { try { if (dt.TableName.Equals("Summary")) { Console.WriteLine("Summary Table"); // 拆分参数为独立变量,方便调试定位 var balanceSum = dt.AsEnumerable().Sum(dr => dr.Field<long>("Balance")); var total1Sum = dt.AsEnumerable().Sum(dr => dr.Field<long>("Total1")); var total2Sum = dt.AsEnumerable().Sum(dr => dr.Field<long>("Total2")); // 注意:如果要把汇总行存入当前DataTable,需要先把对应数值列的类型修改为Int64(long),否则大数值汇总必然出现存储溢出 dt.Rows.Add( null, null, null, null, null, null, balanceSum, total1Sum, total2Sum ); } } catch (Exception ex) { Console.WriteLine("Exception thrown in the AddTrailer - Summary"); throw; } return dt; }
提示:如果业务要求汇总行必须存在当前DataTable中,优先修改对应数值列的数据类型为
Int64,不要强行把long类型的汇总值转成int存储,会出现数值截断错误。
内容的提问来源于stack exchange,提问作者KVN
相关产品推荐
相关产品推荐

