核心循环应避免隐式装箱拆箱,即禁用值类型与object/非泛型接口的隐式转换;优先使用泛型集合、泛型约束、Span/Memory,外提转换逻辑,规避params object[]、反射Invoke及非泛型LINQ。

核心循环内避免隐式装箱和拆箱,本质是防止操作数栈(evaluation stack)因频繁堆内存分配与类型转换而出现水位剧烈波动——这不仅增加GC压力,还可能引发栈帧膨胀、缓存失效甚至栈溢出风险。关键不在于“禁止转换”,而在于切断值类型与 object 或非泛型接口之间的隐式通道。
用泛型集合替代非泛型容器
非泛型集合(如 ArrayList、HashTable)内部存储 object,每次添加或读取值类型都会触发装箱/拆箱。
-
错例:
ArrayList list = new ArrayList(); list.Add(42); int x = (int)list[0];→ 每次 Add 和索引访问都装箱+拆箱 -
正解:改用
List<int>、Dictionary<string, double>等泛型类型,编译器生成专用 IL,全程栈上操作,无对象包装
避免在循环中将值类型赋给 object 或接口变量
哪怕只是临时记录、日志或条件判断,只要在 for/while 循环体内出现 object o = i; 或 IFormattable f = d;,就会在每次迭代重复堆分配。
- 把这类转换提到循环外(如果语义允许),或用泛型方法封装逻辑
- 若必须多态调用,优先使用泛型约束(
T : IComparable<T>)而非object或IComparable
慎用 params object[] 和反射调用
Console.WriteLine("val={0}", i) 或 MethodInfo.Invoke(obj, new object[]{x}) 在循环中极易成为装箱热点。
- 对固定格式日志,改用
string.Create()或Span<char>构造,绕过object[]参数数组 - 避免在热路径中调用
Invoke;可用表达式树预编译委托,或直接硬编码调用
检查 LINQ 与高阶函数的底层行为
像 list.Select(x => x * 2).ToArray() 看似简洁,但若 list 是 ArrayList,则 x 是 object,强制拆箱发生在 lambda 内部——且每次枚举都重复。
- 确保源集合是泛型类型(
List<int>),再配合AsSpan()或Memory<int>进行零分配遍历 - 对简单计算,直接用 for 循环 + 数组索引,比 LINQ 更可控、更贴近栈行为

















