CH5 基元类型、引用类型与值类型 .net 复习课
2009-02-23 18:50
274 查看
5.1 基元类型 Primitive Type
- 编译器直接支持的类型
- C#中的基元类型和FCL中的类型有直接的对应关系,例如: int对应System.Int32
- 如果两个类型之间的转换是“安全”的,即,转换不会造成数据丢失,C#允许进行隐式转换。
5.1.1 Checked与Unchecked基元类型操作
- CLR只在32位和64位上进行算术运算,所以如下代码会导致溢出的发生:
Byte b = 100;
b = (Byte) b + 200;
- CLR的IL指令提供了add和add-ovf这两个指令,add-ovf做Overflow检查,add不做。
- C#默认情况下溢出检查是关闭的。
- C#编译器控制溢出检查的方法是使用/checked+命令行开关。(对于所有代码)
- C#中的checked和unchecked操作符可以让我们只对部分代码做溢出检查。
b = checked( (Byte) (b+200) ); //会抛出System.OverflowException异常
- C#中还有checked和unchecked语句来控制代码块的溢出检查
checked{
/// 需要做溢出检查的代码段
}
* 注意:因为checked不会对代码块中的方法调用中可能出现的溢出做检查。
× System.Decimal在C#中不是基元类型,所以checked、unchecked语句,编译器开关和相关操作符对其没有影响。
5.2 引用类型和值类型
- 引用类型:从托管堆上分配内存空间,会有额外的附加成员需要被初始化,会导致垃圾回收
- 值类型:轻量级类型,从线程堆栈上分配内存空间
- 所有的值类型都必须继承自System.ValueType
- System.ValueType又继承自System.Object
- 值类型不能继承自基类,但可以实现一个或多个接口
- 值类型在以下情况时能获得更好的性能
- 不需要继承基类
- 不会被继承
- 不会被频繁的作为方法的参数
- 不会被频繁的作为方法的返回
- 不会被频繁的用于ArrayList、HashTable之类的集合
5.3 值类型的装箱与拆箱
- 装箱 Boxing
由于值类型是轻量级的类型,所以它们的内存空间被分配在线程的堆栈中。而在托管堆中没有指向它们的指针,这也就意味着无法从托管堆中访问值类型。而在很多
情况下我们需要这样的访问通路。例如我们把值类型存储在一个Array中。于是,我们需要把值类型装箱以供托管堆上的对象访问。
- 装箱操作由以下几步组成
- 分配实例本身大小的内存空间(在托管堆上),以及额外的空间用来存放方法表指针和SyncBlockIndex(用来协同多个线程)
- 拷贝值实例的字段到托管堆
- 返回新分配对象的地址,也就是一个引用。
- 拆箱 Unboxing
- 获得托管对象的引用,若为null,则抛出NullReferenceException异常。若不是目标值类型,则抛出InvalidCastException异常。
- 从中获得值类型字段的地址,即返回一个指向值类型自动的指针。到此拆箱结束。
- (将字段拷贝到线程堆栈上的值类型实例)p = (Point)o;
例子
public static void Main(){
int v = 5;
Object o = v; //一次装箱
v = 123;
Console.WriteLine( v+ ", " + (int) o); // 显示“123, 5” 2次装箱。这里调用了String.Concat()方法,该方法没有int类型的重载。所以,v在这里要装箱。而(int)o在拆箱后还要装一次箱。
}
以上代码执行3次装箱。
改进1:
Console.WriteLine( v + "," + o); // 少了一次拆箱,一次字段拷贝,一次装箱
改进2:
Console.WriteLine(v.ToString() + ", " + o); //v.ToString()方法调用时不需要进行装箱或者拆箱的操作。
- 很多FCL类的方法都提供了基元类型的重载版本,其目的就是减少一些常用值类型的装箱操作
- 如果我们知道自己的代码会导致编译器反复对一个值类型进行装箱,我们可以自己来做装箱操作:
Console.WriteLine("{0},{1},{2}",v,v,v);
Object o = v;
Console.WriteLine("{0},{1},{2}",o,o,o);
- 如果我们希望有一个指向值类型实例的引用,该实例就必须被装箱。
- 将一个未装箱的值类型实例转型为一个该类型实例的接口类型也需要对该实例进行装箱。
Point p = (Point) p.Clone();
ICloneable c = p;
- 如果值类型中重写了System.ValueType的方法,在调用这些方法是,不会发生装箱。
- 编译器直接支持的类型
- C#中的基元类型和FCL中的类型有直接的对应关系,例如: int对应System.Int32
- 如果两个类型之间的转换是“安全”的,即,转换不会造成数据丢失,C#允许进行隐式转换。
5.1.1 Checked与Unchecked基元类型操作
- CLR只在32位和64位上进行算术运算,所以如下代码会导致溢出的发生:
Byte b = 100;
b = (Byte) b + 200;
- CLR的IL指令提供了add和add-ovf这两个指令,add-ovf做Overflow检查,add不做。
- C#默认情况下溢出检查是关闭的。
- C#编译器控制溢出检查的方法是使用/checked+命令行开关。(对于所有代码)
- C#中的checked和unchecked操作符可以让我们只对部分代码做溢出检查。
b = checked( (Byte) (b+200) ); //会抛出System.OverflowException异常
- C#中还有checked和unchecked语句来控制代码块的溢出检查
checked{
/// 需要做溢出检查的代码段
}
* 注意:因为checked不会对代码块中的方法调用中可能出现的溢出做检查。
× System.Decimal在C#中不是基元类型,所以checked、unchecked语句,编译器开关和相关操作符对其没有影响。
5.2 引用类型和值类型
- 引用类型:从托管堆上分配内存空间,会有额外的附加成员需要被初始化,会导致垃圾回收
- 值类型:轻量级类型,从线程堆栈上分配内存空间
- 所有的值类型都必须继承自System.ValueType
- System.ValueType又继承自System.Object
- 值类型不能继承自基类,但可以实现一个或多个接口
- 值类型在以下情况时能获得更好的性能
- 不需要继承基类
- 不会被继承
- 不会被频繁的作为方法的参数
- 不会被频繁的作为方法的返回
- 不会被频繁的用于ArrayList、HashTable之类的集合
5.3 值类型的装箱与拆箱
- 装箱 Boxing
由于值类型是轻量级的类型,所以它们的内存空间被分配在线程的堆栈中。而在托管堆中没有指向它们的指针,这也就意味着无法从托管堆中访问值类型。而在很多
情况下我们需要这样的访问通路。例如我们把值类型存储在一个Array中。于是,我们需要把值类型装箱以供托管堆上的对象访问。
- 装箱操作由以下几步组成
- 分配实例本身大小的内存空间(在托管堆上),以及额外的空间用来存放方法表指针和SyncBlockIndex(用来协同多个线程)
- 拷贝值实例的字段到托管堆
- 返回新分配对象的地址,也就是一个引用。
- 拆箱 Unboxing
- 获得托管对象的引用,若为null,则抛出NullReferenceException异常。若不是目标值类型,则抛出InvalidCastException异常。
- 从中获得值类型字段的地址,即返回一个指向值类型自动的指针。到此拆箱结束。
- (将字段拷贝到线程堆栈上的值类型实例)p = (Point)o;
例子
public static void Main(){
int v = 5;
Object o = v; //一次装箱
v = 123;
Console.WriteLine( v+ ", " + (int) o); // 显示“123, 5” 2次装箱。这里调用了String.Concat()方法,该方法没有int类型的重载。所以,v在这里要装箱。而(int)o在拆箱后还要装一次箱。
}
以上代码执行3次装箱。
改进1:
Console.WriteLine( v + "," + o); // 少了一次拆箱,一次字段拷贝,一次装箱
改进2:
Console.WriteLine(v.ToString() + ", " + o); //v.ToString()方法调用时不需要进行装箱或者拆箱的操作。
- 很多FCL类的方法都提供了基元类型的重载版本,其目的就是减少一些常用值类型的装箱操作
- 如果我们知道自己的代码会导致编译器反复对一个值类型进行装箱,我们可以自己来做装箱操作:
Console.WriteLine("{0},{1},{2}",v,v,v);
Object o = v;
Console.WriteLine("{0},{1},{2}",o,o,o);
- 如果我们希望有一个指向值类型实例的引用,该实例就必须被装箱。
- 将一个未装箱的值类型实例转型为一个该类型实例的接口类型也需要对该实例进行装箱。
Point p = (Point) p.Clone();
ICloneable c = p;
- 如果值类型中重写了System.ValueType的方法,在调用这些方法是,不会发生装箱。
相关文章推荐
- 值类型和引用类型是.net里面的一个基本概念
- .NET中的六个重要概念:栈、堆、值类型、引用类型、装箱和拆箱
- 读《.NET(C#):理解值类型/引用类型,装箱/拆箱,Object类》 有感,小白类文
- 一起谈.NET技术,引用类型赋值为null与加速垃圾回收
- .Net中引用类型按值参数引用的问题
- CH4 类型基础 .net 复习课
- 《CLR via C#》读书笔记 之 基元类型、引用类型和值类型
- 6个重要的.NET概念:栈,堆,值类型,引用类型,装箱,拆箱
- 关于.NET中的值类型和引用类型
- .NET中六个重要的概念:栈、堆、值类型、引用类型、装箱和拆箱 (转)
- .NET 中的引用类型 string
- [转]6个重要的.NET概念:栈,堆,值类型,引用类型,装箱,拆箱
- 读书笔记_CLR.via.c#第五章_基元类型_引用类型_值类型
- .NET中的六个重要概念:栈、堆、值类型、引用类型、装箱和拆箱
- .NET(C#):理解值类型/引用类型,装箱/拆箱,Object类
- 【译】.NET中六个重要的概念:栈、堆、值类型、引用类型、装箱和拆箱
- .NET 基础 一步步 一幕幕[面向对象之堆、栈、引用类型、值类型]
- .net 笔试常见题(一) 值类型与引用类型的区别
- 【译】.NET中六个重要的概念:栈、堆、值类型、引用类型、装箱和拆箱
- 【.Net】浅谈C#中的值类型和引用类型