持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第4天,点击查看活动详情
其实这是一个很低级的错误,大概率都会犯这种错误!一次想明白了,以后就可以杜绝这种低级错误。
错误重现
开门见山,想必看到文章标题就已经可以猜的差不多了:集合被修改后,无法再进行迭代循环操作
为使错误重现,写一段简单的代码
public static void Main(string[] args)
{
var list = new List<FData>()
{
new FData{Code="N09092",Name="ADMIN"},
new FData{Code="N23213",Name="XLSEE"},
new FData{Code="N45444",Name="TESTS"},
new FData{Code="N66697",Name="WWWQW"},
new FData{Code="N11123",Name="FOOS"}
};
foreach(var item in list)
{
if (item.Name == "WWWQW") list.Remove(item);
Console.WriteLine("Code: {0}, Name: {1}\n", item.Code, item.Name);
}
Console.ReadLine();
}
public class FData
{
public string Code { get; set; }
public string Name { get; set; }
}
代码执行后,报错如下
看到错误第一感觉是懵的,这也会报错?
想都没想,直接看代码是不是自己写错了,或者编译有问题
反复测试后,确定就是我代码有问题,而且是大问题!
既然删除不行,那就修改,这下不报错了
再尝试新增(其实这种需求也有,比如再某一条数据后追加一条数据)
错误原因分析
经过上面三个方向的测试,基本得出,增删都会报错,而修改却没有问题....猜也能猜出来,离真相不远了!
分析:在删除了一项,集合的元素个数是变化的。新增也会使它长度发生变化。
而在长度发生变化后,此时元素会重排,第二个元素的索引由1变为0(假设删除的是第一个元素),后面的依次往前移动。而此时Count的值也-1
而在foreach中,索引和Count的值是不允许被修改的,否则内部对比机制就会抛出异常!
在修改元素时,无论怎么修改,索引和Count值不会发生改变的!
既然这样,那我想到一个sao操作
foreach (var item in list)
{
var i = list.Count - list.IndexOf(item) - 1;
var temp = item;
item = list[i];
list[i] = temp;
Console.WriteLine("Code: {0}, Name: {1}\n", item.Code, item.Name);
}
想必写过冒泡算法的对这个不会陌生
我把首尾元素对调一下
猜猜看看可行不可行?
直接看结果
直接报错,编译器都编译不过去....
list[i] = temp;虽然没报错,但是跟他上一行差不多,估计运行时也会报错!
没事,咱还有sao操作,继续折腾
foreach (var item in list)
{
var i = list.Count - list.IndexOf(item) - 1;
var temp = item;
item.Code = list[i].Code;
item.Name = list[i].Name;
list[i].Code = temp.Code;
list[i].Name = temp.Name;
Console.WriteLine("Code: {0}, Name: {1}\n", item.Code, item.Name);
}
这下完美了,通过了编译,并且成功运行!
说明了我前面的分析,并且item中有其他属性也是不能修改的(比如索引或者指针之类的...这里不深究了)
解决办法
既然有问题,除了分析问题,还要解决问题
直接弃用foreach做这种操作不就行了....这活你干不了,有的是人能干!
可以用for循环
for (int i = 0; i < list.Count; i++)
{
if (list[i].Name == "WWWQW") list.Remove(list[i]);
Console.WriteLine("{0} Code: {1}, Name: {2}\n", i, list[i].Code, list[i].Name);
}
其实最保险的方式是:从后往前移除,这样前面的索引就不会变了,变的只是count <--- 听我的准没错!
将IEnumerable先转化为数组(Array),然后再执行删除/新增操作。
这里调用的就是Array的ForEach方法
Array.ForEach(list.ToArray(), x =>
{
if (x.Name == "WWWQW") list.Remove(x);
Console.WriteLine("Code: {0}, Name: {1}\n", x.Code, x.Name);
});
其实这种方法不太好写,而且感觉是为了用而用。
不过需求能实现
至于其他肯定还有方法,比如Linq还没出场呢
这里就不过多介绍了
文末总结
生活处处是学问,也许不经意的常见的一个小东西,蕴含着大道理。
解决问题不是终点,还需要sao操作一把,摸透,才能慢慢接近真象,记忆才会深刻,理解才会彻底!
其实很多时候就像断案一样,把握细节,方能找出蛛丝马迹
反正这玩意,多思考,多换几个方向,大胆猜测,又不要钱[dog]