简言之,C#中的各种变量类型会被分为值类型与引用类型两大类,即有

值类型包括:整型(Int16,Int32,Int64),浮点型(Single,Double,Decimal),结构体(Struct),枚举(Enum),布尔(Boolean),字符型(Char)

引用类型包括:字符串(String),数组(Array),类(Class)

二者的区别在于,值类型的数据会被存放于栈空间,引用类型的数据会被存放于堆空间,这也就最直接地造成了如下的直观区别:

int a = 10;
int b = a;

int[] arr1 = new int[] {1,2,3,4,5};
int[] arr2 = arr1;

Console.WriteLine("a={0},b={1}",a,b);
Console.WriteLine("arr1[0]={0},arr2[0]={1}",arr1[0],arr2[0]);

b=20;

arr2[0] = 90;

Console.WriteLine("a={0},b={1}",a,b);
Console.WriteLine("arr1[0]={0},arr2[0]={1}",arr1[0],arr2[0]);

可以发现当我们对值类型和引用类型分别进行赋值时,可以打印出同样的内容,但当我们修改新变量的值时,旧的值类型变量的值不因新的值类型变量被赋新值而变化,仍保留原值不变,而旧的引用类型的值则会因为新的值类型变量被赋新值而跟随变化。

从本质上解释,对于存放于栈的值类型,当我们进行变量间的相互赋值时,实际上是在站内新开辟一个空间,然后进行复制拷贝,新的变量值的变化不会影响原变量的值,而引用类型则不同,当我们新声明并初始化一个值类型时,会在堆内新开辟一片空间用于存放数据,而在栈内则会存放指向堆空间内对应数据的地址,即arr1,当我们新声明一个arr2(并没有进行new的初始化操作),并将arr1赋值给arr2时,实际上只是在栈空间内存放并拷贝了一个指向同一片堆空间的地址,即arr1与arr2指向的是同一片空间,所以,当对arr2的内容进行修改时,实际上修改了堆内的数据,这样一来,arr1的对应数据也会对应修改。

当然,如果我们对于再对arr2进行初始化,那么就相当于又开辟了一片新的堆空间,此时arr2指向了新的堆空间,那么它的变化也就不会影响到arr1了。

然而,对于同为引用类型的string类型变量,在我们进行上述操作时则会表现出值类型的特征,即:

string str1 = "ABC";
string str2 = str1;
Console.WriteLine(str1);
Console.WriteLine(str2);
str2 = "DEF";
Console.WriteLine(str1);
Console.WriteLine(str2);

这是因为,当我们对str2赋新值的时候,它会自动在堆内再开辟一个新空间用于存放新值,相当于重新new了一个字符串,所以就不会因赋值而影响之前的变量了。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐