首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

淺析 C# foreach 陷阱:不可修改迭代變數、集合變更導致異常

     編輯:關於C#

觸發情境

嘗試在 foreach 迴圈中移除 List<T> 元素:


string temp = name.Text;
foreach (LCourse cou in Data.myCourse)
{
    if (cou.name == temp)
    {
        Data.myCourse.Remove(cou); // 編譯通過,運行拋出 InvalidOperationException
        break;
    }
}

三個實驗驗證行為

實驗一:修改值型別迭代變數


foreach (int i in myArray) { i += 8; }
// 編譯錯誤:CS1656 迭代變數唯讀

實驗二:透過方法修改參考型別內部狀態


class MyClass { private int i; public void Change() { i = 98; } }
foreach (MyClass mc in myArray) { mc.Change(); }
// 編譯通過,但運行拋出 NullReferenceException(陣列元素未初始化)

實驗三:迴圈中移除集合元素且不中斷


List list = new List { "ab", "cd" };
foreach (string s in list) { list.Remove(s); }
// 編譯通過,運行拋出 InvalidOperationException:集合已修改,無法繼續枚舉

核心結論

操作結果原因
賦值給迭代變數 (值型別)編譯錯誤迭代變數為 readonly 複本
呼叫迭代變數方法修改內部狀態 (參考型別)允許修改的是「物件內容」,非變數本身
在迴圈中 Add/Remove 集合元素運行時異常枚舉器偵測集合版本號變更,拋出 InvalidOperationException
移除後立即 break可行枚舉器尚未嘗試 MoveNext,版本檢查未觸發

安全移除模式

  1. 反向 for 迴圈:for (int i = list.Count-1; i >= 0; i--) if (cond) list.RemoveAt(i);
  2. 建立待刪清單:var toRemove = list.Where(x => cond).ToList(); foreach (var x in toRemove) list.Remove(x);
  3. List.RemoveAll:list.RemoveAll(x => cond); (單行、高效、安全)
  4. 迭代器模式:while (enumerator.MoveNext()) { if (cond) { enumerator.Remove(); } } (需 IList 實作)

結語

foreach 設計初衷是「唯讀遍歷」。理解其背後的 IEnumerator 機制(版本號檢查、Current 唯讀),能避免常見運行時異常,並選擇正確的集合操作模式。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved