趕時間,請直接看最後
在開發過程中時常會遇到需要遍歷集合並移除指定項的場景,我們用不同方法來嘗試獲得我們希望的結果。
首先聲明將要用到的 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 會出現下標越界)。
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 方法會返回被移除數量