第五章 基元类型、引用类型和值类型
2013-02-27
ToDo: 什么时候使用值类型,什么时候使用引用类型
5.3 值类型的装箱和拆箱
1 struct Point 2 { 3 public Int32 x, y; 4 } 5 public static class Program 6 { 7 public static void Main() 8 { 9 System.Collections.ArrayList a = new System.Collections.ArrayList();10 Point p;11 p.x = p.y = 1;12 a.Add(p); //对值类型进行装箱,因为Add方法原型:public virtual int Add(object value)13 p = (Point)a[0]; //对已装箱的Point对象进行拆箱14 }15 }
装箱过程:
(1) 在托管堆分配好内存。
(2) 把值类型的字段复制到新分配的堆内存
(3) 返回对象的地址。
拆箱过程:
(1) 获取已装箱的值对象的各个字段地址
(2) 把字段的值复制到基于栈的值类型字段中
值类型调用方法注意:
(1) 若值类型重写基类的虚方法,可以非虚的调用,因为值类型是隐式密封的,没有类从它们派生。
(2) 若要调用非虚的、继承的方法,那么在调用时,值类型实例会装箱,以便通过this【1】指针将对一个对象的引用传给基方法。
(3) 若将值类型的一个未装箱实例转型为类型的某个接口时,要求对实例进行装箱,因为接口变量必须包含对堆上对象的引用
5.3.2 对象的相等性和同一性
System.Object类型提供了一个名为Equals的虚方法,基本实现如下
1 public class Object2 {3 public virtual Boolean Equals(Object obj)4 {5 //若指向同一对象,返回true6 if (this == obj) return true;7 return false;8 }9 }
以上代码实现的是同一性,而非相等性。而且以上实现也不合理,下面展示如何在内部正确实现一个Equals方法:
(1) 若Obj实参为null,就返回false,因为在调用非静态的Equals方法时,this所标识对象不可能为null。
(2) 若this和obj实参引用的是同一对象,返回true
(3) 若this和obj实参引用类型不同,返回false
(4) 若类型定义的每个实例字段,任何一个不相等,返回false
(5) 调用基类型的Equals方法,一般比较它定义的任何字段。
由于System.Object的虚方法Equals可以重写,所以不能调用它来测试同一性,因此,Object提供了一个静态方法ReferenceEquals,原型如下:
1 public class Object2 {3 public static bool ReferenceEquals(object objA, object objB)4 {5 return (objA == objB);6 }7 }
若想检查同一性,务必使用ReferenceEquals,而不是==操作符(除非先把两个操作数都转型为Object),因为==也可以被重载。
在System.ValueType(所有子类型基类)重写了Object.Equals方法,并进行了正确的实现来执行相等性测试,实现如下:
(1) 若obj实参为null,返回false
(2) 若this和obj实参引用不同类型对象,返回false
(3) 对每个实例字段进行比较,任何一个字段不等,返回false
(4) 返回true,ValueType的Equals方法不调用Object的Equals方法。
参考
【1】C# this关键字详解