詳解 .NET Core 遍歷 List 並移除項

趕時間,請直接看最後

在開發過程中時常會遇到需要遍歷集合並移除指定項的場景,我們用不同方法來嘗試獲得我們希望的結果。

首先聲明將要用到的 list 對象。

var list = new List<string>() { "Item1", "Item2", "Item3", "Item3-1", "Item3-2" };
  • 方法1:很大可能性會這些寫,下面的代碼看起來完全沒問題,編譯也能順利通過。只有在運行的時候纔會發現問題:
foreach (var item in list)
{
    if (item.Contains("3"))
        list.Remove(item);
}

報錯信息:System.InvalidOperationException:“Collection was modified; enumeration operation may not execute.”

  • 方法2:改成 for 以後能正常運行了。結果會發現“Item3-1,Item3-3”也輸出了,我們希望是將包含“3”的項移除,結果看起來沒有。
for (int i = 0; i < list.Count; i++)
{
    var item = list[i];
    if (item.Contains("3"))
        list.Remove(item);
}

Console.WriteLine(string.Join(",", list));

輸出:Item1,Item2,Item3-1,Item3-3

循環邏輯重現,可用從下表看出循環只執行了4輪,原因是 list 被移除了兩條後,長度縮短導致位置出現改變:

下標/值 是否滿足條件 length
1 0 / Item1 false 6
2 1 / Item2 false 6
3 2 / Item3 true 5
4 3 / Item3-2 true 4
  • 方法3:將 length 提出來作爲變量,這樣就能將整個 list 遍歷完了。但是這樣會導致下標越界。
var length = list.Count;
for (int i = 0; i < length; i++)
{
    var item = list[i];
    if (item.Contains("3"))
        list.Remove(item);
}

報錯信息:System.ArgumentOutOfRangeException:“Index was out of range. Must be non-negative and less than the size of the collection.

  • 方法4:將 list 複製給新變量,然後再使用遍歷變量新,操作 list。
var listNew = list;
foreach (var item in listNew)
{
    if (item.Contains("3"))
        list.Remove(item);
}

報錯信息:System.InvalidOperationException:“Collection was modified; enumeration operation may not execute.”

和方法一一樣的錯誤,因爲 list 是一個引用類型,listNew 與 list 共用同一個內存地址,所以會報錯(改成 for 會出現下標越界)。

  • 方法5:將 list clone 一次,遍歷 clone 對象時刪除 list 中的子項。
var clone = list.Clone();
foreach (var item in clone)
{
    if (item.Contains("3"))
        list.Remove(item);
}

Console.WriteLine(string.Join(",", list));

輸出:Item1,Item2

這是我們希望得到的結果。

通過 clone 的方法將對象從一個內存地址變爲兩個,通過下面可以看出兩個對象的關係,在 clone 後Ta們就不相等了。

var clone = list;
Console.WriteLine(clone == list);                       //true
Console.WriteLine(ReferenceEquals(clone, list));        //true
Console.WriteLine(ReferenceEquals(clone, list.Clone()));//false
  • 方法6:使用 LINQ RemoveAll 方法可以更簡單
var count = list.RemoveAll(x => x.Contains("3"));
Console.WriteLine(count);
Console.WriteLine(string.Join(",", list));

輸出:
4
Item1,Item2

RemoveAll 方法會返回被移除數量

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章