C#整数溢出默认不报错,仅纯常量表达式溢出触发CS0220;checked仅对直接包裹的整型四则运算和显式转换生效,decimal/float/位运算等不受控。

默认情况下,C# 整数运算溢出不会报错,int.MaxValue + 1 直接变成 int.MinValue,这不是 bug,是设计行为——但你在金融计算、ID 生成或索引边界校验里踩中它,就是严重事故。
什么时候编译器会提前报错(CS0220)
只有一种情况:纯常量表达式溢出。比如 int i = 2147483647 + 10; 编译直接失败,报 CS0220。只要混入任意变量,哪怕只是 const int x = 10;,编译就放过——因为常量折叠规则只对编译期完全可知的字面量生效。
常见误判点:
-
const int MAX = int.MaxValue;看似“常量”,但MAX + 1不触发CS0220,因为int.MaxValue是运行时静态字段,不是编译时常量 -
#define不适用,C# 没有宏常量概念 -
readonly static字段同理,不参与编译期溢出检查
checked 表达式 vs checked 块:作用域和易错写法
checked 只包裹它**文本上直接包含**的运算,不穿透括号或赋值符号。
正确写法:
int result = checked(a + b * c); // 整个表达式受检
checked { int x = a + b; y = x * 2; } // 块内所有整型运算都受检
典型误用:
-
int x = checked(int.MaxValue) + 1;——checked只包住int.MaxValue(无意义),加法仍 unchecked -
int x = checked((long)a + b);—— 转成long后运算已脱离整型溢出范畴,checked实际无效 -
checked { return a + b; }在 void 方法里语法错误,checked块不能单独作语句返回
捕获 OverflowException 的实际要点
OverflowException 是唯一需要捕获的类型,抓 ArithmeticException 或泛 catch Exception 都是错的。
性能敏感路径要警惕:
- 在 tight loop 里用
checked+try/catch,异常开销远高于预判(比如先判断a > int.MaxValue - b) -
decimal运算自带溢出检查,checked(decimal)完全无效,别浪费括号 -
float/double溢出结果是Infinity或NaN,不受checked控制 - 位运算(
&、|、<<、>>)和比较操作(>、==)永远不触发OverflowException,checked对它们无影响
/checked+ 全局开关的风险与补救
项目属性勾选「检查算术溢出/下溢」,等价于整个程序集默认进入 checked 上下文。这会暴露旧代码里依赖绕回逻辑的隐性 bug,比如用 index++ 自动归零做环形缓冲,或用 hash & (size-1) 代替取模——这些在全局 checked 下会直接崩。
补救必须显式且精准:
- 只在出问题的局部用
unchecked { ... }包裹,不要试图关掉整个类或方法 -
unchecked优先级高于外层/checked+,但它只作用于其括号内的代码,不影响前后 - 第三方库源码不可控,若它崩溃,只能联系作者或降级到未启用全局检查的版本
真正容易被忽略的是:checked 不是“安全开关”,它只管整型四则和显式转换;浮点、decimal、引用类型、用户自定义运算符的行为都不归它管——靠它兜底,反而会产生虚假安全感。


















