问题:
在使用 List 时发现一个现象,List.Clear() 和 List=null 会有不一样的运行结果,用一段代码演示一下。
List<int> intSmall= new List<int>() { 1, 2, 3, 4, 5 };
List<List<int>> intLarge = new List<List<int>>();
intLarge.Add(intSmall);
intSmall = null;
//intSmall.Clear();
预想 intSmall = null 和 intSmall.Clear() 的结果应该是相同的,都是仅清空了 intSmall,intLarge 中的 List 不发生改变,但实际运行结果不同。
使用 = null 操作时,是预想的结果,intLarge 内的 List 还是保存着“1,2,3,4,5”这五个数字。
但是,使用 Clear() 操作时,虽然清空的是 intSmall,但 intLarge 中的 List 同样被清空。
原因:
通过查找一些资料,对该现象的原因进行分析。如有错误,欢迎指正。
在分析原因之前先明确 List 的两个要点。
1. List 属于“引用类型”
C#中的类型分为“值类型”和“引用类型”,值类型的存储只需要一段内存,直接存储数据本身,引用类型的存储需要两段内存,一段用于存储数据本身,是存在堆中,另一段是一个引用,指向数据在堆中的位置。
值类型:
引用类型:
而 List 属于引用类型。
2. List.Add() 属于“浅拷贝”
深拷贝:拷贝对象时,不仅把对象的引用进行拷贝,把对象引用的值也一起拷贝,此时拷贝后的对象和源对象相互独立,对其中任何一个对象的改动都不会影响到另一个对象。
浅拷贝:拷贝对象时,仅拷贝对象的引用,而拷贝对象和源对象还是引用同一个数据,此时拷贝后的对象和源对象之间存在联系,改变其中任何一个对象都会影响到另一个对象。
而 List.Add() 属于浅拷贝。
3. 问题的原因
基于前面两个,对问题原因进行分析。
intSmall 与内部数据的对应关系如下。
intLarge.Add(intSmall) 时,进行的是浅拷贝,仅拷贝了 intSmall 的引用。
此时,如果对 intSmall 进行 Clear,删除的是栈中的数据,因为 intLarge 中的 List 也是指向这些数据,因此 intLarge 中的 List 也为空。
如果对 intSmall 进行 = null,只是将 intSmall 的引用置为 null,并不会改变该引用所指向的实际数据。因此,此时 intLarge 内的 List 的数据还在。