但在调用非虚的、继承的方法(GetType 或 MemberwiseClone) 时

时间:2022-06-27 07:39:21

C# 中的基元类型、值类型和引用类型 1. 基元类型(Primitive Type)

  编译器直接撑持的类型称为基元类型。基元类型可以直接映射到 FCL 中存在的类型。例如,int a = 10 中的 int 就是基元类型,其对应着 FCL 中的 System.Int32,上面的代码你完全可以写作System.Int32 a = 10,编译器将生成完全形同的 IL,也可以理解为 C# 编译器为源代码文件中添加了 using int = System.Int32。

1.1 基元类型的算术运算的溢出检测

  对基元类型的大都算术运算都可能产生溢出,例如

byte a = 200; byte b = (Byte)(a + 100);//b 此刻为 4

  上面代码生成的 IL 如下

但在调用非虚的、继承的方法(GetType 或 MemberwiseClone) 时

  从中我们可以看出,在计算之前两个运算数都被扩展称为了32位,然后加在一起是一个32位的值(十进制300),该值在存到b之前又被转换为了Byte。C# 中的溢出查抄默认是*的,所以上面的运算并不会抛出异常或孕育产生错误,也就是说编译器生成 IL 时,默认选择加、减、乘以及转换操纵的无溢出查抄版本(如上图中的 add 命令以及conv.u1都是没有进行溢出查抄的命令,其对应的溢出查抄版天职别为add.ovf和conv.ovf),这样可以使得代码快速的运行,但前提是开发人员必需保证不产生溢出,或者代码能够预见溢出。
  C#中控制溢出,可以通过两种方法来实现,一种全局设置,一种是局部控制。全局设置可以通过编译器的 /checked 开关来设置,局部查抄可以使用 checked/unchecked 运算符来对某一代码块来进行设置。进行溢出查抄后如果产生溢出,会抛出 System.OverflowException 异常。通过上述设置后编译器编译代码时会使用加、减、乘和转换指令的溢出查抄版本。这样生成的代码在执行时要稍慢一些,因为 CLR 要查抄这些运算是否产生溢出。
  使用溢出查抄

checked{ byte a = 200; byte b = (Byte)(a + 100); } //亦可以通过下面的方法来实现 // byte b = checked((Byte)(a + 100));

但在调用非虚的、继承的方法(GetType 或 MemberwiseClone) 时

最佳实践: 在开发措施时打开 /checked+ 开关进行调试性生成,这样系统会对没有显式符号为 checkedunchecked 的代码进行溢出查抄,此时产生异常便可以轻松捕捉到,及时修正代码中的错误 ,正式颁布时使用编译器的 /checked- 开关,确保代码能够快速运行,不会孕育产生溢出异常。

2. 值类型和引用类型

但在调用非虚的、继承的方法(GetType 或 MemberwiseClone) 时

  CLR 撑持两种类型:值类型和引用类型,下面引用 MSDN 对两者的界说:
  

2.1 值类型

  值类型直接包罗它的数据,值类型的实例要么在仓库上,要么在内联布局中。与引用类型对比,值类型更为"轻",因为它们不需要在托管堆上分配内存,亦不受垃圾回收器的控制,无需进行垃圾回收,C#中的值类型都派生自System.ValueType ,值类型主要包孕两种类型:布局枚举, 布局可以分为以下几类:

数值类型

bool 类型

char 类型

用户自界说的布局

  值类型的特点:

所有的值类型都直接或间接的派生自 System.ValueType

值类型都是隐式密封的,即不能从其它任何类型担任,也不能派生出任何的类型,目的是防备将值类型用作其它引用类型的基类型。

将值类型赋值给此外一个值类型的变量时,会逐字段进行复制。

每种值类型都有一个默认的结构函数来初始化该类型的默认值。

  自界说类型时,什么情况下适合将类型界说为值类型?

类型具有基元类型的特点,即该类型十分简单,没有成员会改削类型的任何实例字段

类型不需要从其它类型担任,亦不派生出任何的类型

类型的实例字段较小(16字节或更小)

类型的实例较大(大于16字节),但不作为要领的实参通报,也不从要领返回。

  对付后两点是因为实参默认以传值的方法进行通报,造成对值类型中的字段进行复制,造成性能上的损害。被界说为返回一个值类型的要领返回时,实例中的字段会复制到挪用者的分配的内存中,对性能造成损害。

值类型的装箱和拆箱

  装箱:将值类型转换为引用类型的过程称为 装箱(Box).
  对值类型实例进行装箱时所产生的工作如下所示:
  1. 在托管堆中分配内存。分配的内存量是值类型各字段所需的内存量,还要加上托管堆所有东西都有的两个特别成员(类型东西指针和同步块索引)所需的内处量。
  2. 值类型的字段复制到新分配的堆内存中
  3. 返回东西的地点。此刻该地点是东西的引用;值类型酿成了引用类型。