第 5 章 基元类型、引用类型和值类型
第 5 章 基元类型、引用类型和值类型
NyxX第 5 章 基元类型、引用类型和值类型
本章内容:
5.1 编程语言的基元类型
基元类型
编译器直接支持的数据类型称为 基元类型 基元类型直接映射到 Framework 类库(FCL)中存在的类型。
只有在转换“安全”的时候,C#才允许隐式转型。
5.2 引用类型和值类型
值类型和引用类型的一些区别
- 值类型有两种表示形式:未装箱和已装箱,引用类型总是已装箱。
- 值类型从System.ValueType派生,重写了Equals和GetHashCode方法。
- 由于不能将值类型作为基类型来定义新的值类型或者新的引用类型,所以不应在值类型中引入任何新的虚方法。所有方法都不能是是抽象的,所有方法都隐式密封(不可重写)。
- 值类型的变量总是包含基础类型的一个值,且成员初始化为0;而引用类型的变量包含堆中对象的地址,创建时默认初始化为null 。
- 将值类型变量赋给另一个值类型变量会执行逐字段复制,而将引用类型变量赋给另一个引用类型变量只复制内存地址。
- 引用类型的多个变量可能引用堆中同一个对象,而值类型变量是独立的。
- 由于未装箱的值类型不在堆上分配,一旦定义了该类型的一个实例的方法不再活动,为它们分配的存储就会被释放,而不是等着进行垃圾回收。
5.3 值类型的装箱和拆箱
装箱过程
- 在托管堆中分配内存。分配的内存量是值类型各字段所需的内存量,还要加上托管堆所有对象都有的两个额外成员(类型对象指针和同步索引块)所需的内存量。
- 值类型的字段复制到新分配的堆内存。
- 返回对象地址。现在该地址是对象引用;值类型成了引用类型。
C# 编译器检测到上述代码是向要求引用类型的方法传递值类型,所以自动生成代码对对象进行装箱。
拆箱
CLR 分两步完成复制。
第一步获取已装箱对象中各个字段的地址。这个过程称为拆箱。
第二步将字段包含的值从堆复制到基于栈的值类型实例中。
如果重写的虚方法要调用方法在基类中的实现,那么在调用基类的实现时,值类型实例会装箱
5.3.2 对象相等性和同一性
同一性
两个引用是否指向同一个对象。
相等性
在同一性的基础上增加了每个实例字段的比较。
5.4 对象哈希码
重写 Equals 就必须重写 GetHashCode ,确保相等性算法和对象哈希码算法一致。
5.5 dynamic 基元类型
使用dynamic类型可以调用成员操作,比如字段、属性、方法等。编译器会生成特殊的IL代码(有效载荷),在运行时根据实际类型执行相应操作。
如果字段,方法参数或方法返回值的类型是 dynamic,编译器会将该类型转换为 System.Object ,并在元数据中向字段、参数或返回类型应用 System.Runtime.CompilerServices.DynamicAttribute 的实例
如果局部变量被指定为 dynamic,则变量类型也会成为 Object,但不会向局部变量应用 DynamicAttribute
编译器通常不允许将表达式从 Object 隐式转型为其他类型,必须显式转型。但是,编译器允许使用隐式转型语法将表达式从 dynamic 转型为其他类型。
dynamic 和 var 区别
- var 声明局部变量只是一种简化语法,它要求编译器根据表达式推断具体数据类型。var关键字只能在方法内部声明局部变量。
- dynamic 关键字可用于局部变量、字段和参数。
- 表达式不能转型为var,但能转型为dynamic。
- 必须显示初始化用var声明的变量,但无需初始化用dynamic 声明的变量。





