5.9 KiB
万物之父和装箱与拆箱
object
概念:
object 是 System.Object 的别名
在 C# 的统一类型系统里,可以把它理解成==万物之父==
因为==所有类型最终都能当成 object 来看待==
通常用法:
1. 作为所有类型的公共父类型来理解 C# 类型系统
2. 在不知道具体类型时,临时用 object 接收数据
3. 理解为什么值类型可以被当成“对象”使用
核心理解:
1. 引用类型赋值给 object 时,通常只是==把引用当成 object 看待==
2. 值类型赋值给 object 时,会发生==装箱==
3. 所以“能放进 object”不代表底层过程都一样
案例:
string str = "hello";
object o1 = str; // 引用类型赋值给 object,不是装箱
int num = 10;
object o2 = num; // 值类型赋值给 object,发生装箱
为什么说 object 是万物之父
可以先这样记:
- 类最终都继承自
object - 数组、委托等引用类型也都可以当成
object - 值类型虽然不是普通类,但最终也能通过装箱当成
object
所以从使用角度看:
- ==所有类型最终都能统一到
object这一层==
常见成员:
ToString()把对象转成字符串Equals(object obj)比较两个对象是否相等GetType()获取对象运行时类型GetHashCode()获取哈希码ReferenceEquals(a, b)判断两个引用是不是指向同一个对象
例子:
int num = 123;
object obj = num;
Console.WriteLine(obj.ToString());
Console.WriteLine(obj.GetType());
Console.WriteLine(obj.Equals(123));
补充:
ToString()、Equals()、GetType()、GetHashCode()是最常见的基础成员ReferenceEquals是object提供的静态方法
装箱(Boxing)
概念:
装箱就是==把值类型转换成 object 或它实现的接口类型==
运行时会把这个值==复制一份==,包装成一个对象放到托管堆上
通常用法:
1. 把值类型当成对象处理
2. 把值类型传给接收 object 的 API
3. 把值类型放进某些非泛型容器里
语法:
int num = 10;
object obj = num; // 隐式装箱
object obj2 = (object)num; // 显式写法,但一般没必要
案例:
int a = 123;
object obj = a; // 装箱
a = 456;
Console.WriteLine(a); // 456
Console.WriteLine(obj); // 123
理解:
装箱时放进 object 的,是当时那个值的一份副本
所以后面 a 变了,不会影响已经装箱进去的 obj
注意:
- ==装箱是值类型 ->
object或接口类型== - 装箱会产生额外对象分配,通常比普通赋值更耗性能
- 引用类型赋值给
object不叫装箱
拆箱(Unboxing)
概念:
拆箱就是==把 object 里的值类型取出来,还原成原来的值类型==
拆箱是==显式转换==
语法:
object obj = 123; // 装箱
int num = (int)obj; // 拆箱
案例:
object obj = 100;
int num = (int)obj;
Console.WriteLine(num);
核心理解:
拆箱做的事不只是“强转一下”
它会先检查 obj 里面装着的是不是目标值类型
检查通过后,再把值复制出来
常见错误:
object obj = 10;
short s = (short)obj; // 报错
原因:
obj 里装箱进去的真实类型是 int
拆箱时要求==类型先对上==
所以应该先拆成 int,再按需要继续转换
正确写法:
object obj = 10;
short s = (short)(int)obj;
再补一个坑:
object obj = null;
int num = (int)obj; // 运行时报错
注意:
- ==拆箱一定是显式的==
- ==拆箱时目标类型必须和装箱时的值类型匹配==
- 对
null拆箱会报错 - 类型不匹配时会抛出转换异常
object 和装箱拆箱的关系
最核心的一句话:
==值类型之所以也能被当成“对象”来处理,就是因为有装箱和拆箱==
对比理解:
int num = 10;
object o1 = num; // 装箱
string str = "hello";
object o2 = str; // 不是装箱,只是引用转换
所以要分清:
- 引用类型 ->
object通常只是“把它当父类看” - 值类型 ->
object通常会真的发生装箱
性能和使用建议
为什么要注意装箱拆箱:
- 装箱会分配新对象
- 拆箱要做类型检查和复制
- 大量发生时,会增加 GC 压力和性能开销
常见容易发生装箱的场景:
- 使用非泛型集合,比如
ArrayList - 方法参数写成
object - 值类型转成接口类型时
示意:
ArrayList list = new ArrayList();
list.Add(1); // 装箱
list.Add(2); // 装箱
int num = (int)list[0]; // 拆箱
更推荐:
List<int> list = new List<int>();
list.Add(1);
list.Add(2);
这样通常可以避免很多不必要的装箱拆箱。
一句话记忆
- ==
object是万物之父,所有类型最终都能统一到它这一层== - ==值类型进
object叫装箱,从object还原值类型叫拆箱== - ==引用类型给
object不等于装箱==
引用:
- Microsoft Learn: Built-in reference types - The object type
- Microsoft Learn: Boxing and Unboxing (C# Programming Guide)
- Microsoft Learn: Types - C# language specification
- Microsoft Learn: Object.GetType Method
- Microsoft Learn: System.Object.ToString method
- Microsoft Learn: System.Object.Equals method
#核心 #object #装箱拆箱