(1)总的区别
| 值类型
| 引用类型
|
内存分配地点
| 分配在栈中 | 分配在堆中 |
效率
| 效率高,不需要地址转换 | 效率低,需要进行地址转换 |
内存回收
| 使用完后,立即回收 | 使用完后,不是立即回收,等待GC回收 |
赋值操作
| 进行复制,创建一个同值新对象 | 只是对原有对象的引用 |
函数参数与返回值
| 是对象的复制 | 是原有对象的引用,并不产生新的对象 |
类型扩展
| 不易扩展 | 容易扩展,方便与类型扩展 |
(2)赋值区别
1 static void Main(string arg[])
2
3 {
4
5 int x=10;
6
7 int y=x;
8
9 Console.writeline(x.ToString()+","+y.ToString());//输出的结果是:10,10
10
11 y=20;
12
13 Console.writeline(x.ToString()+","+y.ToString());//输出的结果是:10,20
14
15 int [] arry=new int [1];
16
17 int [] arry1=arry;
18
19 arry[0]=10;
20
21 Console.writeline(arry[0].ToString()+","+arry1[0].ToString());//输出的结果是:10,10
22
23 arry1[0]=20;
24
25 Console.writeline(arry[0].ToString()+","+arry1[0].ToString());//输出的结果是:10,20
26
27 string str1="liao:;
28
29 string str2=str1;
30
31 Console.writeline(str1+","+str2);//输出的结果是:liao,liao
32
33 str1="xiao";
34
35 Console.writeline(str1+","+str2);//输出的结果是:xiao,liao
36
37 }
38
1)因int 是值类型,int x int y 内存中就有两个地方存储x,y;所以当改变x或y的值时,都不会改变另一个变量的值。
2)因int [] arry,int []arry1 是数组,数组是引用类型,所以这两个变量引用了同一个对象(数组)
所以当 改变某一个引用指向的对象的属性同时也会影响到所有其他指向这个对象的引用,所以当arry1[0]的值改成20时,arry[0]的值也随着改变成20;最后的arry[0]和arry[1]的值都为20;不过虽然字符类型string也是引用类型,但它的工作方式更像值类型,既当str1的值改成"xiao"时,新创建了一个string对象,str1引用这个新的string对象;str2仍然引用原来string对象。产生这种行为的原因是string对象是恒定的,也就是说,一旦一个string对象被创建,它的值就不能再修改,所以当改变一个字符串变量的值的时候,仅仅是新创建了一个包含修改内容的新的string对象。
(3)作为函数参数或者返回值
1 class Program
2 {
3 public static void add(int k)
4 {
5 k = k + 39;
6 }
7 public static void add(ref int i)
8 {
9 i = i + 39;
10
11 }
12 public static void add(int[] j)
13 {
14 j[0] = j[0] + 39;
15 }
16 public static void astr(ref string sy)
17 {
18 sy = "ddd";
19 }
20 static void Main(string[] args)
21 {
22
23 int[] ary = new int[1];
24 int[] ary1 = ary;
25 ary[0] = 1;
26 add(ary[0]);
27 Console.WriteLine(ary[0].ToString()); //输出的结果是:1
28 add(ref ary[0]);
29 Console.WriteLine(ary[0].ToString()); //输出的结果是:40
30 add(ary);
31 Console.WriteLine(ary[0].ToString()); //输出的结果是:79
32 string str4 = "liaoxiaol";
33 astr(ref str4); //输出的结果是:ddd //str4的值已改变
34 Console.WriteLine(str4);
35
36 }
37 }
38
说明:
1)对于值类型的变量:要想在函数中对传进去的参数做真正的修改,需要借助于ref这个关键字
所以当执行add(ary[0])后,arry[0]的值时并没有改变;而当执行add(ref ary[0])后,ary[0]的值
就变成了1+39=40;
2)对于引用类型的变量,则和值变量相反;而在实际应用中我们并不想引用类型变量作为函数参数传入后,其值有所心改变,要做到这一点需要为“引用类型”提供一个额外的clone函数
所以当执行add(arry)后,arry[0]的值就变成了40+39=79;
3)虽然string类型也是引用类型但前面也说过string 类型的工作更像值类型
附加:对于引用类型来说,提供一个clone函数不是一件容易的事情,尤其出现引用类型嵌套的时候,
在C#中,尤其自己定义类型的时候,常常由于是用struct来定义还是用class来定义,即是定义一个值类型还是一个引用类型呢。在这本书上给了几个判定条件,如果如下几点都满足的话,建议用struct来定义为值类型,否则用class定义为引用类型。
1)这个类型是否主要为了数据存储;
2)是否只通过属性来访问对象的数据成员;
3)这个类型是否不会有子类型;
4)在程序处理的时候不会把这个类型对象通过多态来处理。
总而言之,在C#中,就是把底层面的数据用值类型来处理,而包含复杂操作,需要进行扩展的数据用引用类型来处理