【问题标题】:Code won't exit foreach block代码不会退出 foreach 块
【发布时间】:2010-05-28 03:00:57
【问题描述】:

我有以下 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,然后它就停止..没有错误,只是停止..

感谢您的帮助:)

【问题讨论】:

  • 一个建议:布尔属性应以'Is'为前缀,并像这样使用if(ready.IsUpdateReady)

标签: c# list


【解决方案1】:

在迭代其枚举器时,您正在修改 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);
      }
}

【讨论】:

  • 我同意。在你修改它循环的东西之后,foreach 循环应该做什么?从头开始,还是从原来的地方开始?好像你想说'while (!PlayerLog.empty()) { etc etc },然后慢慢地从 PlayerLog 中删除元素。
【解决方案2】:

这是一个使用 lambda 表达式的简单方法:

TempLog.Clear();
TempLog.AddRange(PlayerLog.Where(a => a.UpdateReady));
TempLog.ForEach(a => PlayerLog.Remove(a));

【讨论】:

    【解决方案3】:

    奇怪,当您尝试更改您正在迭代的集合时,它应该只是抛出一个异常。我会这样做:

    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里面。

    【讨论】:

    • RemoveAll 是否存在以数组为参数的重载?
    • 我原以为有,但显然我的记忆力已经让我失望了。即将更新...遗憾的是没有,因为我不必手动迭代数组或满足竞争条件:(
    • 对,应该有。
    【解决方案4】:

    正如 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 循环之后将它们全部删除。

    【讨论】:

      【解决方案5】:

      Mark 是对的,但是使用 for 循环这样就不会浪费内存了。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-05-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多