值类型和引用类型

用几个案例来解释值类型和引用类型

首先看值类型,案例如下:

案例一:

 1 static void Main(string[] args)
 2         {
 3             int n = 10;
 4             M1(n);
 5             Console.WriteLine(n);//10
 6             Console.ReadKey();
 7         }
 8 
 9         private static void M1(int m)
10         {
11             m++;
12         }
View Code

解释:第一次声明变量n,会在栈中开辟一块空间,我们自己命名为n,赋值后,空间中就存了10.调用方法的时候,把n传给方法,相当于把n赋值给m,所以m变成10,然后执行方法内部的代码(m++),此时m=11。但是我们要输出的变量是n,所以结果仍然是10.

案例二:

 1 static void Main(string[] args)
 2         {
 3             #region 案例一
 4             //int n = 10;
 5             //M1(n);
 6             //Console.WriteLine(n);//10
 7             #endregion
 8 
 9             #region 案例一
10             Person p = new Person();
11             p.Age = 100;
12             M2(p);
13             Console.WriteLine(p.Age);//101
14             #endregion
15 
16             Console.ReadKey();
17         }
18 
19         private static void M2(Person p1)
20         {
21             p1.Age++;
22         }
View Code

解释:Person p=new Person();首先在堆中开辟一块空间,保存person对象,给Age赋值为100,这块堆的内存名称为x00x32,保存在栈中,对应的栈名称我们取名为p。当调用M2()方法的时候,我们重新声明了一个变量p1,并且把p赋给p1(变量都是值类型),等效于把栈中的值复制一份存入p1这块栈中,所以p,p1都指向x00x32这块内存,执行方法中的代码后,p.Age,p1.Age都为101。

案例三:

 1 class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             #region 案例一
 6             //int n = 10;
 7             //M1(n);
 8             //Console.WriteLine(n);//10
 9             #endregion
10 
11             #region 案例二
12             //Person p = new Person();
13             //p.Age = 100;
14             //M2(p);
15             //Console.WriteLine(p.Age);//101
16             #endregion
17 
18             #region 案例三
19             Person p = new Person();
20             p.Age = 100;
21             M3(p);
22             Console.WriteLine(p.Age);//
23             #endregion
24 
25 
26             Console.ReadKey();
27         }
28 
29         private static void M3(Person p1)
30         {
31             p1.Age = 1000;
32             p1 = new Person();
33             p1.Age = 200;
34         }
35 
36         private static void M2(Person p1)
37         {
38             p1.Age++;
39         }
View Code

结果为1000.

案例四:

 1 static void Main(string[] args)
 2         {
 3             #region 案例一
 4             //int n = 10;
 5             //M1(n);
 6             //Console.WriteLine(n);//10
 7             #endregion
 8 
 9             #region 案例二
10             //Person p = new Person();
11             //p.Age = 100;
12             //M2(p);
13             //Console.WriteLine(p.Age);//101
14             #endregion
15 
16             #region 案例三
17             //Person p = new Person();
18             //p.Age = 100;
19             //M3(p);
20             //Console.WriteLine(p.Age);//1000
21             #endregion
22 
23             #region 案例四
24             Person p = new Person();
25             p.Age = 100;
26             M4(p);
27             Console.WriteLine(p.Age);//100
28             #endregion
29 
30             Console.ReadKey();
31         }
32 
33         private static void M4(Person p1)
34         {
35             p1 = new Person();
36             p1.Age++;
37         }
View Code

Person p=new Person();在堆中开辟一块空间x001,Age=100,把堆的名称x001保存到栈中,栈中开辟的空间(实际的名称没显示)我们自己命名为p。调用方法时,又在栈中开辟了一块空间,我们自己命名为p1,然后把p1赋值给p,由于变量名称都是值类型,所以直接把p中x001拷贝一份给p1,这时候p,p1都指向x001这块堆空间。接下来执行方法中的代码的时候,又在堆中开辟了一块空间x002(类成员Age的初始默认值为0,p1.Age++后为1),然后赋值给p1,这时候,p1中保存堆名称变成x002了.但是我们输出的是p.Age,所以仍然是100.

案例五:

 1      {
 2             #region 案例五
 3             string name = "科比";
 4             M5(name);
 5             Console.WriteLine(name);//科比
 6             #endregion
 7 
 8             Console.ReadKey();
 9         }
10 
11         private static void M5(string name1)
12         {
13             name1 = "乔丹";
14         }        
View Code

注意:变量都是值类型的,存在栈中 。

案例六:

 1     {
 2             #region 案例六
 3             int[] arrInt = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 };
 4             M6(arrInt);
 5             //??????????
 6             for (int i = 0; i < arrInt.Length; i++)
 7             {
 8                 Console.WriteLine(arrInt[i]);//1,2,3,4,5,6,7,8
 9             }
10             #endregion
11 
12             Console.ReadKey();
13         }
14 
15         private static void M6(int[] arrInt1)
16         {
17             arrInt1 = new int[arrInt1.Length];
18             for (int i = 0; i < arrInt1.Length; i++)
19             {
20                 arrInt1[i] = arrInt1[i] * 2;
21             }
22         }        
View Code

int[] arrInt = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 };先在堆中开辟一块空间x001,并且初始化数组。栈中开辟的空间保存x001,我们自己把栈中空间命名为arrInt,调用方法的时候,声明了变量arrInt1,在栈中又开辟了一块空间x002,我们命名为arrInt1,然后把arrInt赋值给arrInt1,因为变量都是值类型,所以直接把栈的内容拷贝一份到arrInt1中,此时arrInt,arrInt2都指向x001这块内存。接下来执行方法中的代码。在M6方法中,先在堆中开辟了一块空间x002,指定了数组的长度,但是没有初始化,由于是int类型,所以数组的所有元素都是0,然后把x002保存到栈中,此时arrInt1就不再指向x001,而是指向了x002,最后操作数组arrInt1。我们需要输出的是arrInt,所以最后的值是1,2,3,4,5,6,7,8。

案例七:

 1 {
 2             #region 案例七
 3             int[] arrInt = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 };
 4             M7(arrInt);
 5             //??????????
 6             for (int i = 0; i < arrInt.Length; i++)
 7             {
 8                 Console.WriteLine(arrInt[i]);
 9             }
10             #endregion
11 
12             Console.ReadKey();
13         }
14 
15         private static void M7(int[] arrInt1)
16         {
17             for (int i = 0; i < arrInt1.Length; i++)
18             {
19                 arrInt1[i] = 100;
20             }
21         }        
View Code

接下来我们把上面的方法全部改成引用传递,看看有什么异同。

案例一:

 1   #region 案例一
 2             int n = 10;
 3             M1(ref n);
 4             Console.WriteLine(n);
 5             #endregion
 6 
 7 private static void M1(ref int m)
 8         {
 9             m++;
10         }
View Code

 int n=10;在栈中开辟一块空间x001,存入10,我们给这块空间的名称重新命名为n,值传递会把内存中的东西(不管保存的是内容还是地址)拷贝一份,引用传递传递的是电脑给内存分配的地址x001,所以值传递等效于我们给x001这个地址又重新起了个名字m, m,n都对应x001这个内存名称,x001对应分配的内存,但是m,n都不保存。所以m++后,n也会变。(至于m,n为什么不保存需要研究)。所以最终输出11.

把代码转换为IL语言后,发现内存中是不存在m,n变量的,只有内存地址,程序是根据内存地址读取数据的。

所以值传递传递的是栈中内容,对于值类型,栈中的类型就是栈中的数据,对于引用类型,栈中的内容就是对象的地址。引用传递传递的是栈本身的地址,多个变量名实际上指向的是同一个栈变量。

 案例二:

 1             #region 案例二
 2             Person p = new Person();
 3             p.Age = 100;
 4             M2(ref p);
 5             Console.WriteLine(p.Age);//101
 6             #endregion
 7 
 8 private static void M2(ref Person p1)
 9         {
10             p1.Age++;
11         }
View Code

结果是101;

案例三:

 1 #region 案例三
 2             Person p = new Person();
 3             p.Age = 100;
 4             M3(ref p);
 5             Console.WriteLine(p.Age);//
 6             #endregion
 7 
 8 private static void M3(ref Person p1)
 9         {
10             p1.Age = 1000;
11             p1 = new Person();
12             p1.Age = 200;
13         }
View Code

Person p=new Person();在堆中开辟一块空间x001,Age=100,保存在栈中,栈这块内存地址是y001,里面保存的内容是x001,我们自己命名为p,调用方法时,采用引用传递,只是给栈的地址换了个别名,改为p1,p,p1还是指向栈中y001这块内存。执行方法后,在堆中重新分配了块内存x002,并且赋值给p1,所以栈中x001这块内存中存的内容变成x002,然后改变Age=200,到这里实际上p,p1都指向了x002这块内存,所以最终的结果为200.

案例四:

 1  #region 案例四
 2             Person p = new Person();
 3             p.Age = 100;
 4             M4(ref p);
 5             Console.WriteLine(p.Age);//
 6             #endregion
 7 
 8 private static void M4(ref Person p1)
 9         {
10             p1 = new Person();
11             p1.Age++;
12         }
View Code

结果:1;

案例五:

 1 #region 案例五
 2             string name = "科比";
 3             M5(ref name);
 4             Console.WriteLine(name);//
 5             #endregion
 6 
 7 private static void M5(ref string name1)
 8         {
 9             name1 = "乔丹";
10         }
View Code

结果是:乔丹

案例六:

 1  int[] arrInt = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 };
 2             M6(ref arrInt);
 3             //??????????
 4             for (int i = 0; i < arrInt.Length; i++)
 5             {
 6                 Console.WriteLine(arrInt[i]);//
 7             }
 8 
 9 private static void M6(ref int[] arrInt1)
10         {
11             arrInt1 = new int[arrInt1.Length];
12             for (int i = 0; i < arrInt1.Length; i++)
13             {
14                 arrInt1[i] = arrInt1[i] * 2;
15             }
16         }
View Code

结果全部是0;

案例七:

 1  #region 案例七
 2             int[] arrInt = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 };
 3             M7(ref arrInt);
 4             //??????????
 5             for (int i = 0; i < arrInt.Length; i++)
 6             {
 7                 Console.WriteLine(arrInt[i]);
 8             }
 9             #endregion
10 
11  private static void M7(ref int[] arrInt1)
12         {
13             for (int i = 0; i < arrInt1.Length; i++)
14             {
15                 arrInt1[i] = 100;
16             }
17         }
View Code

全部是100

原文地址:https://www.cnblogs.com/wesley168/p/6406473.html