C#List的赋值问题的解决

2022-11-13 13:11:35 list 赋值 解决

问题描述

如下图所示,query1是个集合,把它赋值给了query2,当移除query2里面数据的时候,query1对应的数据也被移除了。

在这里插入图片描述

原因分析:

对此猜测是引用类型的问题,类似于浅拷贝深拷贝那种概念。

基础概念:

对于上述的情况,要怎么去赋值,以及不同的写法对应的结果是什么样的呢,我做了如下测试 ,在看结果之前,先了解一下相关概念。

C#中的堆和栈:(指的是程序运行时的内存区域)

内存分为堆区域和栈区域,栈空间比较小,但是读取速度快(先进后出),堆空间比较大,但是读取速度慢。

栈区:存放函数的参数,局部变量,返回数据等值,会自动释放。

堆区:存放着引用类型的对象,会由GC来自动释放。

值类型和引用类型

值类型:在方法传递的时候,传递的是自身的“拷贝”。
(例如:结构体struct,数据类型short/int/double/bool,枚举类型enum,可空类型)

引用类型:引用类型则是传递的自身的“地址”。
( 例如:数组,类,接口,委托,object,string)

值类型只需要一段单独的内存(此处指栈区内存),用于存储实际的数据。

引用类型需要两段内存,第一段存储实际的数据,位于堆中。第二段是一个引用,存储在栈里,指向数据在堆中的存放位置。

特点:
1、值类型变量声明后,不管是否已经赋值,编译器为其分配内存。
2、引用类型当声明一个类时,只在栈中分配一小片内存用于容纳一个地址,而此时并没有为其分配堆上的内存空间。当使用 new
创建一个类的实例时,分配堆上的空间,并把堆上空间的地址保存到栈上分配的小片空间中。
3、值类型的实例通常是在线程栈上分配的(静态分配),但是在某些情形下可以存储在堆中。(某些情况指的是:作为字段时,跟随其所属的变量(实例)存储;作为局部变量时,存储在栈上。)
4、引用类型的对象总是在进程堆中分配(动态分配)。

本地测试:

下图展示了4个list以及对应的赋值情况,从结果可以看出,list1、list3、list4的栈区内容都指向了同一个堆地址,所以当这三个list任意一个删除数据或添加数据,其他两个也会变化。而list2由于指向的堆地址跟其他三个不同,所以list2的数据变化不会影响到list1、list3、list4。

在这里插入图片描述

其中list4比较出乎我的意料,印象中list t=new list中的关键字new,会在托管堆上重新分配空间,并返回一个该空间的地址,但是从结果上来看,list4还是指向了list1的堆地址。猜测可能是编译器的自动优化?就像string的赋值那样?

下面是string的测试,图一是定义了三个变量,a、b、c,可以看出,a和b虽然是分别定义并且分别赋值的,但由于值一样,最终还是指向了同一个堆地址。而图二中,给a重新赋值后,发现它并没有去更改原地址中的数据,而是在堆中开辟了一块新的空间,并指向了这个新的堆地址(在栈中的地址没变)。

在这里插入图片描述

在这里插入图片描述

相关文章