簡體   English   中英

避免InvalidOperationException的最佳實踐:集合已修改?

[英]Best practice to avoid InvalidOperationException: Collection was modified?

我經常需要這樣的東西:

 foreach (Line line in lines)
 {
    if (line.FullfilsCertainConditions())
    {
       lines.Remove(line)
    }
 }

這是行不通的,因為由於在循環期間更改了枚舉數,所以我總是收到InvalidOperationException

因此,我將所有此類循環更改為以下內容:

List<Line> remove = new List<Line>();
foreach (Line line in lines)
{
   if (line.FullfilsCertainConditions())
   {
      remove.Add(line)
   }
}

foreach (Line line in remove) {
{
   lines.Remove(line);
}

我不確定這是否是最好的方法,因為在最壞的情況下,我必須對原始列表進行2次迭代,因此它需要2n而不是n的時間。

有一個更好的方法嗎?

編輯:

我可以使用Mark的答案來做到這一點!但是,如果我的集合沒有實現RemoveAll()怎么辦?

例如一個

System.Windows.Controls.UIElementCollection

編輯2:

再次在Mark的幫助下,我現在可以進行以下調用以刪除所有ScatterViewItem:

CollectionUtils.RemoveAll(manager.getWindow().IconDisplay.Items, elem => elem.GetType() == typeof(ScatterViewItem));

將其直接烘焙到List<T>

lines.RemoveAll(line => line.FullfilsCertainConditions());

或在C#2.0中:

lines.RemoveAll(delegate(Line line) {
    return line.FullfilsCertainConditions();
});

在非List<T>情況下(您對問題的編輯),您可以像下面這樣包裝(未經測試):

static class CollectionUtils
{
    public static void RemoveAll<T>(IList<T> list, Predicate<T> predicate)
    {
        int count = list.Count;
        while (count-- > 0)
        {
            if (predicate(list[count])) list.RemoveAt(count);
        }
    }
    public static void RemoveAll(IList list, Predicate<object> predicate)
    {
        int count = list.Count;
        while (count-- > 0)
        {
            if (predicate(list[count])) list.RemoveAt(count);
        }
    }
}

由於UIElementCollection實現了(非通用的) IList因此應該可以使用。 而且非常方便,使用C#3.0,您可以在IList / IList<T>之前添加一個this ,並將其作為擴展方法。 唯一的妙處是,匿名方法的參數將是object ,因此您需要將其拋棄。

您只需將原始列表替換為過濾后的列表即可:

lines = lines.Where(line => line.FullfilsCertainConditions()).ToList();

建立新的清單:

public IList<Line> GetListWithoutFullfilsCertainConditions(IList<Line> fullList) 
{
    IList<Line> resultList = new List<Line>(fullList.Count);

    foreach (Line line in fullList)
    {
       if (!line.FullfilsCertainConditions())
       {
          resultList.Add(line)
       }
    }

    return resultList;
}

您也可以只使用while循環。

int i = 0;
while(i < lines.Count)
{
  if (lines[i].FullfilsCertainConditions())
  {
     lines.RemoveAt(i);
  }
  else {i++;}
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM