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