值类型性能优化关键在于避免装箱、控制复制并合理使用场景:用泛型替代object参数、Span替代Substring、谨慎定义struct大小与字段,禁用隐式分配。

值类型本身不慢,慢的是让它被迫装箱、复制失控或误用场景。 优化核心不是“多用struct”,而是让值类型待在它该待的地方:栈上、缓存行里、零拷贝切片中。
为什么Dictionary<object string></object>比Dictionary<int string></int>慢10倍
每次用int当key,都会触发一次装箱:堆上分配一个带对象头和方法表的Int32实例,再复制值过去。查找时还要拆箱——两次堆内存操作+类型检查。而Dictionary<int string></int>全程在栈/内联存储,Key比较就是CPU寄存器级整数比对。
- 泛型字典必须用值类型Key,别图省事写
object或dynamic - 日志方法接收
object参数?改用泛型重载:Log<t>(string msg, T value)</t>,编译期擦除装箱 - 旧API强制要
object?用Unsafe.AsRef<t>()</t>绕过装箱(仅限极少数受控场景)
Span<t></t>替代string.Substring()避免隐式分配
string.Substring()返回新字符串,必然堆分配;而ReadOnlySpan<char>.Slice()</char>只是指针偏移+长度调整,零分配、零拷贝。这对解析协议头、日志字段提取、CSV分隔等高频切片场景效果立竿见影。
- 别写
input.Substring(5, 10),改用input.AsSpan().Slice(5, 10) - 传参时优先用
ReadOnlySpan<byte></byte>而非byte[],尤其在底层IO、加密、序列化函数中 -
Span<t></t>不能跨await边界——它绑定栈帧,异步方法里要用Memory<t></t>替代
struct定义不当反而拖慢性能
不是所有小类都适合改成struct。.NET对值类型的优化有明确前提:大小适中(建议≤16字节)、无引用字段、不常被装箱、不作为泛型约束频繁传递。
- 含
string、List<t></t>等引用字段的struct,复制时只拷贝指针,但语义上仍依赖GC管理——失去值类型优势 - 超过32字节的
struct按引用传递更高效(JIT会自动优化),硬传可能引发大量栈拷贝 - 实现
IComparable或IEquatable<t></t>时,若内部调用object.Equals(),等于悄悄开了装箱后门
真正卡住性能的,往往不是“没用Span”,而是foreach循环里反复new StringBuilder()、日志方法里把enum转object再拼进字符串、或者用ArrayList存千个int——这些地方连分配点都藏在语法糖下面,得靠[MemoryDiagnoser]才能揪出来。



















