C# 值类型和引用类型

C# 值类型和引用类型
 
内存中主要分为两块空间: 栈空间和堆空间。他们都是用来存储数据的。不同类型的数据存储在不同的地方。值类型和引用类型划分依据是他们在内存中存储的结构不同。
 
 
值类型
  所有的数值类型(long  int  short byte decimal double float......)、char、bool、枚举、结构均为值类型
  
 
引用类型
  string (是一个特殊的引用类型)、数组和类均为引用类型。
 
 
值类型和引用类型的区别
1、首先在Test命名空间下定义了一个ClassTest类和一个SturctTest的结构体,如下面代码所示:
 1 namespace Test
 2 {
 3     /// <summary>
 4     /// ClassTest 类
 5     /// </summary>
 6     public class ClassTest
 7     {
 8         //名字
 9         public string Name;
10     }
11 
12     /// <summary>
13     /// StructTest 结构体
14     /// </summary>
15     public struct StructTest
16     {
17         //名字
18         public string Name;
19     } 
20 }
 
2、在程序的入口调用它们,如下面代码所示:
 1 namespace Test
 2 {
 3     class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             //创建 ClassTest 对象,并为对象中的Name字段赋值
 8             ClassTest c1 = new ClassTest();
 9             c1.Name = "我是c1";
10 
11             //创建 StructTest 对象,并为对象中的Name字段赋值
12             StructTest s1 = new StructTest();
13             s1.Name = "我是s1";
14         }
15     }
16 }
View Code

 

我们来看看这时候这两个对象在内存空间中是如何存在的:

从这张图可以看出:

  1、类实例化出来的对象,真实的值是存储在堆空间中的,栈空间中的变量存储的只是这个对象在堆空间中的一个引用地址。

      2、结构实例化出来的对象,真实的值是直接存储在栈空间中的变量中。

 

3、接下来我们在上面代码的基础上加几行代码:

首先定义一个ClassTest类型的变量c2,并将c1的值赋给该变量,再改变c2的Name字段的值。再定义一个StructTest类型的变量s2,并将s1的值赋给该变量,再改变s2的Name字段的值。

 1 namespace Test
 2 {
 3     class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             //创建 ClassTest 对象,并为对象中的Name字段赋值
 8             ClassTest c1 = new ClassTest();
 9             c1.Name = "我是c1";
10             //新加代码
11             ClassTest c2 = c1;
12             c2.Name = "我是c2";
13             Console.WriteLine("c1的Name为:{0},c2的Name为:{1};", c1.Name, c2.Name);//结果:第一个值为:我是c2,第二个值为:我是c2
14 
15 
16             //创建 StructTest 对象,并为对象中的Name字段赋值
17             StructTest s1 = new StructTest();
18             s1.Name = "我是s1";
19             //新加代码
20             StructTest s2 = s1;
21             s2.Name = "我是s2";
22             Console.WriteLine("s1的Name为:{0},s2的Name为:{1};", s1.Name, s2.Name);//结果:第一个值为:我是s1,第二个值为:我是s2
23         }
24     }
25 }
View Code

从最终结果来看,对类和结构做同样的操作,最终的值的结果不一样。这是为什么呢?我们来看下加了代码之后,变量在内存中的变化情况,如下图:

根据图可以看出,因为类是引用类型,所以将c1变量赋值给c2变量,其实只是把存在变量中的引用地址赋值给了c2变量,这时候c2和c1指向堆空间中同一个对象,修改c2的值,c1的值也会受影响。而结构是值类型,因为变量的值本身是存在该变量上,将s1变量赋值给s2变量,是拷贝了一份出来赋给s2变量,这时候修改s2变量的值是不会影响s1变量的值。

所以无论是值类型还是引用类型的变量之间相互赋值,都是将源变量中的值拷贝一份,将这个副本赋值给目标变量。而区别在于

  • 值类型:因为值类型的变量中存储的是真实的值,所以拷贝传递的也是真实的值。赋值完成以后,对一个变量的修改不会影响另外的1个变量的值。
  • 引用类型:因为引用类型的变量中存储的是对象的地址,所以拷贝传递的也是地址。赋值完毕以后,两个变量指向了同一个对象。对1个变量进行修改对另外的变量有影响。
 
局部变量及成员变量
局部变量:
      相同点: 无论局部变量是值类型的还是引用类型的,那么这个局部变量始终是开辟在栈空间中的.
      不同点: 值类型变量的真实的值,直接存储在栈空间中的变量中.
                  引用类型的变量,对象是存储在堆空间中的,栈空间中的变量中存储的是对象在堆空间中的地址.
 
成员变量:
  成员变量除于对象。无论这个成员变量是值类型的还是引用类型,都跟着对象一起开辟在堆空间中。这个成员变量存储值的原理和局部变量一样。
 
posted @ 2015-08-08 17:24  Chen_Weifeng  阅读(293)  评论(0)    收藏  举报