我有以下C#代码段,它接受一个列表,找到准备更新的对象,然后将它们推入临时列表,从主列表中删除,然后继续它的快乐方式。我的问题是循环通过我的主列表的foreach块不会退出。
TempLog.Clear(); //Ensure TempLog is empty
foreach (CLogger ready in PlayerLog)
{
if (ready.UpdateReady == true) // Record is ready to be updated in database
{
TempLog.Add(ready); // Add record to templog
PlayerLog.Remove(ready); // Remove from playerlog
}
}
<---- Never reaches this point
if (TempLog.Count > 0) // Just check that templog isn't empty
{
new Thread(Update).Start(); // Run update code
}
我已经进行了大量的调试,我可以看到PlayerLog从1开始,TempLog为0,然后进入foreach循环,选择记录UpdateReady标志打开,TempLog转到1,PlayerLog转到0,然后它就会停止..没有错误,只是停止..
感谢您的帮助:)
答案 0 :(得分:14)
当您迭代其枚举器时,您正在修改PlayerLog,这是一个很大的禁忌。
更好的选择是迭代PlayerLog的副本,同时修改原始的PlayerLog:
foreach (CLogger ready in new List<PlayerLogType>(PlayerLog))
{
if (ready.UpdateReady) // Please don't compare boolean values against true...
{
TempLog.Add(ready);
PlayerLog.Remove(ready);
}
}
或者,您可以使用传统的for循环。就个人而言,我喜欢从最后开始,因为我知道我将删除项目,所以我不必重新调整索引:
for (int i = PlayerLog.Count - 1; i >= 0; i--)
{
var ready = PlayerLog[i];
if (ready.UpdateReady)
{
TempLog.Add(ready);
PlayerLog.RemoveAt(i);
}
}
答案 1 :(得分:1)
奇怪的是,当你试图改变你正在迭代的集合时,它应该抛出异常。我这样做:
var ready = PlayerLog.Where(c => c.UpdateReady).ToArray();
if (ready.Any())
{
TempLog.Clear(); //Ensure TempLog is empty
TempLog.AddRange(ready);
foreach (CLogger c in ready) PlayerLog.Remove(c);
new Thread(Update).Start(); // Run update code
}
此外,这看起来有点像你正在尝试构建生产者/消费者队列。如果是这样,你应该看看微软的Concurrency and Coordination Runtime,现在奇怪的是Microsoft Robotics Studio。
答案 2 :(得分:1)
这是一个使用lambda表达式的简单方法:
TempLog.Clear();
TempLog.AddRange(PlayerLog.Where(a => a.UpdateReady));
TempLog.ForEach(a => PlayerLog.Remove(a));
答案 3 :(得分:0)
正如Mark指出的那样,当您修改正在循环的集合时,foreach循环经常会中断。
在这种情况下,您可以避免复制列表,因为您有一个临时列表可供使用。如果TempLog的最终内容无关紧要,请尝试:
TempLog.Clear(); // initial "new" list
foreach (CLogger ready in PlayerLog)
{
if (ready.UpdateReady != true)
TempLog.Add(ready); // item being kept (not removed) - add to "new" list
}
if (TempLog.Count != PlayerLog.Count) // check if any items were not kept
{
PlayerLog.Clear();
PlayerLog.AddRange(TempLog);
new Thread(Update).Start(); // Run update code
}
另一个解决方案是将要删除的项目列表存储在队列或堆栈中,然后在foreach循环后将它们全部删除。
答案 4 :(得分:0)
Mark是对的,但是使用for循环,这样就不会浪费内存。