c# 值类型与引用类型的区别
参考资料:《C#程序设计》
1、首先我们先了解一下c#的基本数据类型包含哪些。
c#的数据类型可分为两种,分别是值类型和引用类型。关系如下图:
2、什么是值类型呢?
值类型直接存储数据值,它主要包括简单类型和复合类型两种,其中,简单类型是程序中使用的最基本类型,主要包括整数类型、浮点类型、布尔类型和字符类型等4种。值类型在栈中进行分配,因此效率很高,使用值类型主要目的是为了提高性能。值类型具有如下特性。
》值类型变量都存储在栈中。
》访问值类型变量时,一般都是直接访问其实例。
》每个值类型变量都有自己的数据副本,因此对一个值类型变量的操作不会影响其他变量。
》值类型变量不能为null,必须具有一个确定的值。
3、什么又是引用类型呢?
引用类型是构建C#应用程序的主要对象类型数据,引用类型的变量又称为对象,可存储对实际数据的引用。C#支持两个预定义的引用类型object和string。
在应用程序执行的过程中,引用类型使用new关键字创建对象实例,并存储在堆中。堆是一种由系统弹性配置的内存空间,没有特定大小及存活时间,因此可以被弹性地运用于对象的访问。
引用类型具有如下特征。
必须在托管堆中为引用类型变量分配内存。
在托管堆中分配的每个对象都有与之相关联的附加成员,这些成员必须被初始化。
引用类型变量是由垃圾回收机制来管理的。
多个引用类型变量可以引用同一对象,这种情形下,对一个变量的操作会影响另一个变量所引用的同一对象。
引用类型被赋值前的值都是null。
所有被称为“类”的都是引用类型,主要包括类、接口、数组和委托等。例如:
Student student1=new Student();
Student student1=student1;
4、他们的区别是什么?
先上图,如下
值类型声明的变量int a1,a2的值是存储在栈中的,而引用类型则在栈中存储引用(地址/URL)指向内存堆中,把值存储在堆中相应的对象里。
4.1、概念上讲,值类型是直接存储值的,而引用类型则是对其引用的存储,存的一个地址,这个地址指向内存堆中相应的对象。
4.2、从内存空间上讲,值类型是在栈中操作,而引用类型则在堆中分配存储单元。栈在编译的时候就分配好内存空间,在代码中有栈的明确定义;而堆是程序运行中动态分配的内存空间,可以根据程序的运行情况动态地分配内存的大小。
4.3、因此,值类型总是在内存中占用一个预定义的字节数,而引用类型的变量则在堆中分配一个内存空间,这个内存空间包含的是对另一个内存位置的引用,这个位置是托管堆中的一个地址,即存放此变量实际值的地方。
5、例子
创建一个控制台应用程序,首先在程序中创建一个类stamp,该类中定义两个属Name和Age,其中Name属性为string引用类型,Age属性为int值类型;然后定义一个ReferenceAndValue类,该类中定义一个静态的Demonstration方法,该方法主要演示值类型和引用类型使用时,其中一个值变化时,另外的值是否变化;最后在Main方法中调用ReferenceAndValue类中的Demonstration方法输出结果。代码如下。
class Program
{
static void Main(string[] args)
{
//调用ReferenceAndValue类中的Demonstration方法
ReferenceAndValue.Demonstration();
Console.ReadLine();
}
}
public class stamp //定义一个类
{
public string Name { get; set; } //定义引用类型
public int Age { get; set; } //定义值类型
}
public static class ReferenceAndValue //定义一个静态类
{
public static void Demonstration() //定义一个静态方法
{
stamp Stamp_1 = new stamp { Name = "Premiere", Age = 25 };
stamp Stamp_2 = new stamp { Name = "Again", Age = 47 };
int age = Stamp_1.Age; //获取值类型Age的值
Stamp_1.Age = 22; //修改值类型的值
stamp Stamp_3 = Stamp_2; //获取Stamp_2中的值
Stamp_2.Name = "Again Amend"; //修改引用的Name值
Console.WriteLine("Stamp_1's age:{0}", Stamp_1.Age);//显示Stamp_1中的Age值
Console.WriteLine("age's value:{0}", age); //显示age值
Console.WriteLine("Stamp_2's name:{0}", Stamp_2.Name);//显示Stamp_2中的Name值
Console.WriteLine("Stamp_3's name:{0}", Stamp_3.Name);//显示Stamp_3中的Name值
}
}