发布于2026-07-08 阅读(0)
扫一扫,手机访问

这正是泛型的核心价值所在——它让值类型在集合和调用中始终保持栈上操作,避免了堆分配和内存拷贝。你看,List 的 Add() 参数就是 int,全程栈操作零装箱;而 ArrayList.Add() 接收 object,每次传入 int 都必然装箱,分配堆内存并拷贝值。差异就在编译期生成的专用方法 vs. 运行时的隐式包装。
List 一加整数就装箱因为 List 接收的是 object 参数,传入 int 时编译器必须把它包装成堆上对象——哪怕你只插一个数,也分配一次堆内存、拷贝一次值。这不是语法糖,是 CLR 强制行为。
ArrayList 同理,且没有类型安全,编译期不报错,运行时才可能出 InvalidCastExceptionList 的 Add() 是编译期生成的专用方法,参数是 int,全程栈上操作,零装箱Dictionary 都比 Hashtable 更快:后者 key/value 全是 object,每次取值都要拆箱IComparable.CompareTo 调用时悄悄二次装箱值类型实现接口后,用接口变量调用方法,会触发额外装箱。比如:
int x = 42; IComparable comp = x; // 第一次装箱:x → object 包装成 IComparable comp.CompareTo(100); // 第二次装箱:CompareTo 参数是 object,100 又被装箱
这在循环里尤其危险。真正零成本的做法是直接用泛型约束:
int.CompareTo(int):直接调用值类型原生方法,无接口、无装箱where T : IComparable,让 JIT 生成专用代码,绕过接口虚调用Console.WriteLine 和字符串插值的差异Console.WriteLine(42) 看似简单,但底层匹配的是 WriteLine(object) 重载,42 必然装箱。而 $"Value: {42}" 在 .NET Core 3+ 已优化:对基础类型(int、bool、DateTime 等)直接走格式化逻辑,不经过 object 中转。
string.Format("{0}", 42) 仍会装箱:它的参数是 object[],每个值类型元素都得装Console.WriteLine($"{value}") 安全;但 Console.WriteLine(value.ToString()) 更明确,且不依赖运行时优化void Log(object msg),传 DateTime.Now 就装箱;改成 void Log(T msg) where T : struct 或直接接收 string,就能避开拆箱要求类型严格一致,不是“能转就行”。下面三行看着像,只有最后一行能过:
object o = 42; // int 装箱 int i = (int)o; // ✅ 正确 long l = (long)o; // ❌ InvalidCastException float f = (float)o; // ❌ 同样抛异常,哪怕数值上可表示
更隐蔽的是 null 拆箱:
object o = null; int i = (int)o; 抛 NullReferenceException,不是 InvalidCastExceptionint? nullable = null; object o = nullable; int i = (int)o; 同样崩,因为可空类型装箱后是 null,不是 Nullable 实例object 来源的各类组合真正难防的不是“会不会装箱”,而是“你以为没装,其实悄悄装了”。比如把 struct 当 interface 用、在旧版集合里塞数字、甚至只是多写了一个 (object) 强转。这些地方不报错、不告警,只在压测时 GC 时间突然飙升。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8