C#类型基础----引用类型
前言
上一讲说了一下值类型的部分内容,虽然没有什么有意思的东西能吸引大家的注意,也没啥好的代码让大家参考一下,但是楼主的水平实在有限,各位看官如果不嫌弃的话,还是看看吧,如果对你的生活或者学习有那么一丝帮助的话,都算是楼主的万幸.
正文
当声明一个引用类型变量,并使用new操作符创建引用类型实力的时候,该引用类型的变量会被分配到线程栈上,变量保存了位于堆上的引用类型的实例的内存地址.变量本身不包含任何类型所定义的数据.如果仅仅声明一个变量,但不使用new操作符,由于在堆上还没有创建类型的实例,因此,变量值为null,意思是不指向任何对象(堆上的对象的实例).对于变量的类型声明,用于限制此变量可以保存的类型实例的地址.
说明:这里有一些概念可能容易混淆,那就是变量(Variable),对象(object),实例(Instance).变量可以是一个值类型,也可以是一个引用类型.当变量是引用类型时,由于本身只包含实际对象的引用(内存地址),因此也叫做对象引用.而在对象创建的对象,称为对象的实例(根据类的定义分配了内存).从一段简单的代码也可以看出,如果运行object a=null;Console.WriteLine(a.ToString());,会得到运行时错误”未将对象引用设置到对象的实例”.因此变量a是一个对象引用,因为没有使用new操作符,所以它没有指向任何的对象实例.
如果有一个这样的类,它依然代表直线上的一点:
public class RefPoint
{
public int x;
public RefPoint(int x) { this.x = x; }
public RefPoint() { }
}
当仅仅写下一条声明语句时:
它的效果如下图所示,在线程栈上创建一个不包含任何数据,也不知想任何对象的(不包含内存地址)的变量:
当使用new操作符时:
rPoint1= new RefPoint(1);
则会完成下面几件事情:
1.在应用程序堆上创建一个引用类型对象的实例,并为它分配内存空间.
2.自动传递该实例的运用给构造函数.(正因为如此,才可以在构造函数中使用this来访问这个实例.)
3.调用该类型的构造函数.
4.返回该实例的引用(内存地址),赋值给rPoint1变量,如下图所示:
简单类型
很多时候大家喜欢用int类型作为值类型的实例,用object类型作为引用类型的实例来说明问题.但是今天咱们采用自定义的结构和类,分别对值类型和引用类型进行说明.这是因为简单类型(比如int)有一些框架类库已经实现了的行为,这些行为会让我们对一些操作产生误解.或者说,用他们作为示例还不够纯粹.
举个例子,如果我们想比较两个int类型是否相等,通常会这样:
int i=3;
int j=3;
if(i==j)
Console.WriteLine(“ i equals to j ”);
但是,对于自定义的值类型,比如结构,就不能用”==”来判断它们是否相等,而需要在变量上调用Equals()方法来完成.
再来个例子,大家知道string是一个引用类型,在比较他们是否相等的时候,通常会这样做:
string a=”123456”;
string b=”123456”;
if(a==b)
Console.WriteLine(“a equals to b”)
貌似也能说明引用类型比较相等的时候可以使用”==”,实际上,在后面就能够看到,当使用”==”对引用类型变量进行比较的时候,比较的他们是否指向堆上同一个对象.而上面a,b指向的显然是不同的对象,只是对象包含的值相同,所以可见,对于string类型,对他们的比较实际上比较的是值,而不是引用(string是一种特殊的的引用类型,它的特殊性在于它是不可变类型,以后会说).
为了避免上面这些引起的混淆,在对象判等部分将采用自定义的结构和类来分别说明.
拆装箱
简单来说,装箱就是讲一个值类型转换成等价的引用类型.它的过程分为这样几步:
(1)在堆上为新生成的对象实例分配内存.该对象实例包含数据,但它没有名称.
(2)将栈上值类型变量的值赋值到堆上的对象中.
(3)将对象创建的对象的地址返回给引用类型变量.
因此,当我们运行这样的代码时:
int i=1;
object boxed=i;
Console.WriteLine(“Boxed Point : ”+boxed);
效果如下图所示:
(装箱)
而拆箱则是将一个已装箱的引用类型转换为值类型:
int i=1;
object boxed=i;
int j;
j=(int)boxed;
Console.WriteLine(“UnBoxed Point : ”+j);
需要注意的是拆箱操作分为两步来完成:
(1)获取已装箱的对象的地址.
(2)将值从堆上的对象中复制到堆栈上的值变量中.
可见,拆装箱需要反复在对上进行操作,因此,在程序中应该尽量避免无意义的拆装箱.