C#中List<List<string>>添加子列表后Clear致数据丢失的原因及解决方法
问题解答:List<List>中子列表被清空的原因与解决方法
先看看你贴的代码:
List<List<string>> strs = new List<List<string>>(); List<string> str = new List<string>(); foreach (DataGridViewRow sr in dataGridView1.SelectedRows) { if(i >= 10) { strs.Add(str); //此处strs包含一个含10个元素的str列表 str.Clear(); i = 0; var a = strs; //此处strs包含一个含0个元素的str列表 } str.Add(sr.Cells["MOBILNI"].Value.ToString().Trim()); i++; } strs.Add(str);
这是个非常典型的引用类型认知误区,我给你掰扯明白:
为什么清空原列表会影响外层列表?
在C#里,List<T>属于引用类型,和int、bool这种值类型不一样。当你执行strs.Add(str)的时候,你并没有把str里的10个元素复制一份存到strs里——你存的只是str这个列表对象在内存里的地址。
换句话说,strs里的每一项都和你手里的str变量指向同一个内存对象。所以你调用str.Clear()的时候,相当于直接把那个唯一的列表对象清空了,strs里的所有引用自然都会看到这个被清空的结果。
而当你换成str = new List<string>()时,你是重新创建了一个全新的列表对象,让str变量指向这个新对象。原来那个已经被加到strs里的旧列表完全没被碰,所以strs里的子列表依然保留着之前的10个元素,新的数据会往新列表里存,两者互不干扰。
两种可行的解决方法
方法1:创建新列表替换(推荐)
就是你已经发现的正确方式,每次存满10个元素后,直接给str赋值一个新的List<string>:
if(i >= 10) { strs.Add(str); str = new List<string>(); // 替换成新列表,旧列表不受影响 i = 0; }
这种方式逻辑清晰,内存效率也更高,不需要额外复制元素。
方法2:添加时复制原列表的副本
如果你非要保留str.Clear()的操作,可以在添加到strs的时候,创建一个原列表的副本:
if(i >= 10) { strs.Add(new List<string>(str)); // 传入原列表,创建包含相同元素的新列表 str.Clear(); i = 0; }
这样strs里存的是独立的副本,后续清空原列表也不会影响它。不过这种方式每次都要复制元素,数据量大的时候会有额外的性能开销,所以不如第一种方法实用。
内容的提问来源于stack exchange,提问作者Aleksa Ristic
相关产品推荐
相关产品推荐

