首页 > 代码库 > 数据类型(1) - 值类型和引用类型
数据类型(1) - 值类型和引用类型
和javascript等脚本语言不同,C#基本上可以说是强类型的编程语言。这意味着,任何变量在编译时(而不是运行时)就必须要有一个类型。直到c#4.0,引入了动态类型为止。c#的弱类型体现在其类型object中。任何变量都可以是object类型的,也就是说他们在编译时没有类型,肯定能通过编译。但错误将会在运行时才发生。
强类型的好处有很多,例如可以保证类型安全(否则将会发生运行时错误),可以享受IDE带来的智能提示等,不容易出错(出错可以在编译的时候就知道)。c#有很多数据类型,而他们可以分为两类:ValueType(值类型)和ReferenceType(引用类型)。前者包括int, double, long等等那些数值类型,bool,结构体和枚举。后者则比较抽象,包括类,接口,委托,数组,字符串等。搞清楚两大类数据类型的特性非常重要,这也是一个面试基础题目:
- 所有值类型的默认值都是0,所有引用类型默认值都是Null。这可以反过来作为我们对某种类型究竟是值类型还是引用类型的判断方法。例如字符串是一个容易令人混淆的类型,但只要想到他的默认值是null不是0,就很容易的可以判定它是属于引用类型了。另外,这也是系统自带的构造函数的行为,即为所有值类型设置初始值0,引用类型则是null。
- 值类型初始化分配在栈上(在栈上拥有一块空间,其大小根据值类型的大小而定),且拥有清晰的生命周期,一旦使用完毕立即被回收。引用类型初始化分配则比较复杂,在堆上分配引用类型,其占用空间大小由引用类型本身的大小决定,在栈上加入一个引用,其为一个类似指针的东东,指向堆上那块空间。使用完毕要等垃圾收集器回收,且回收时候不确定。注意,这句话实在是太重要了,所以我再重复一遍,值类型初始化分配在栈上,引用类型初始化分配在堆上,其占用空间大小由引用类型本身的大小决定,另外还要在栈上加入一个引用,其为一个类似指针的东东,指向堆上那块空间。如果初始化没有赋值,则只会在堆上分配一块空间,而没有指针。
- “初始化”的学名是使用new关键字。如果不加参数的话,将调用数据类型自己的默认构造函数。无论是值类型还是引用类型都可以。如果还有其他构造函数,则视你传入的变量的数量和类型,系统决定调用哪个构造函4 数。例如你完全可以int a = new int(),这等效于int a=0,因为new调用类Int32的默认构造函数,而其将值设置为0。值得注意的是,值类型的默认构造函数是不能被覆盖的,你只能定义有参数的构造函数。
- 所有的值类型都不支持继承,而引用类型可以(如果不是密封类)。
- 值类型可以包含引用类型作为其属性,反之亦然。值类型和引用类型都可以实现字段,属性,方法,接口和事件。
- 为值类型赋值时,是进行简单的复制。为引用类型赋值时,是改变指针的指向。例如
int i=0; int j=i;
此时如果改变i的值,j的值将不受影响。但如果i是一个引用类型,则j=i意味着i和j的指针指向相同的地方。所以改变任何一个的值,另一个也会受到影响。这种行为称之为浅复制,即仅仅在栈上复制一个引用,而指针则仍然指向相同的地方(即堆上仍然是只有一个对象但是现在两个引用指向它)。如果要在堆上开辟一个新的对象,则要使用深复制,其方法为实现ICloneable接口。引用类型浅复制的行为是默认的,即使是一个较复杂的值类型,其中包含引用类型(例如你可以写一个包含类的结构体),其行为也是一样。
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 rectangle a = new rectangle("info", 10, 20); 6 rectangle b = a; 7 a._chang = 999; 8 a._recinfo.infostring = "new info"; 9 10 Console.WriteLine(string.Format("A: chang is {0} and kuan is {1}, info: {2}", a._chang, a._kuan, a._recinfo.infostring));11 Console.WriteLine(string.Format("B: chang is {0} and kuan is {1}, info: {2}", b._chang, b._kuan, b._recinfo.infostring));12 Console.ReadKey();13 }14 }15 16 struct rectangle17 {18 public shapeinfo _recinfo;19 public double _chang;20 public double _kuan;21 22 public rectangle(string recinfo, double chang, double kuan)23 {24 _recinfo = new shapeinfo(recinfo);25 _chang = chang;26 _kuan = kuan;27 }28 }29 30 class shapeinfo31 {32 public string infostring;33 34 public shapeinfo(string s)35 {36 infostring = s;37 }
运行代码可以看到两个结构体的shapeinfo的值都变了,而其属于值类型的那两个double则是互相不受影响。
数据类型(1) - 值类型和引用类型