前言
嘿,小伙伴們!
今天,咱們就來聊聊這兩位循環(huán)界的 "老炮兒" —— foreach
循環(huán)和和 for
循環(huán),看看它們到底有何不同!
foreach 循環(huán)的內(nèi)部實現(xiàn)原理
foreach
循環(huán)是 C# 提供的用于簡化集合遍歷的語法,可以說是 C# 為了方便我們這些懶人而發(fā)明的,它讓咱們不用去操心那些煩人的細(xì)節(jié),直接就可以愉快地遍歷集合。
它的內(nèi)部實現(xiàn)依賴于集合對象是否實現(xiàn)了 IEnumerable
或 IEnumerator
接口。
IEnumerable 接口:這個接口就像是個神奇的門把手,輕輕一擰就能打開遍歷的大門,里面定義了一個名為 GetEnumerator
的方法,這個方法會返回一個實現(xiàn)了 IEnumerator
接口的對象。
IEnumerator 接口:這個接口有幾個重要的成員:
MoveNext
方法,用于判斷是否還有下一個元素,就像按電梯按鈕一樣,按一下就能到下一層Current
屬性,獲取當(dāng)前元素的值Reset
方法,將迭代器重置到初始位置,實際中很少使用,就像我家里的那臺老式收音機,雖然還在,但已經(jīng)很久沒用過了
foreach
循環(huán)的基本語法就像這樣:
foreach (var item in collection)
{
// 訪問 item
}
當(dāng)使用 foreach
循環(huán)時,編譯器會生成類似于以下的代碼:
IEnumerator<T> enumerator = collection.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
T item = enumerator.Current;
// 處理 item
}
}
finally
{
IDisposable disposable = enumerator as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
}
說明:以上這段代碼的關(guān)鍵是 GetEnumerator()
方法返回了一個 IEnumerator
實例,這個實例負(fù)責(zé)跟蹤當(dāng)前的元素并能夠移動到下一個元素。
所以 foreach
循環(huán)的內(nèi)部實現(xiàn)可以簡單總結(jié)如下:
foreach
循環(huán)使用迭代器(iterator)來遍歷集合,編譯器會悄悄生成一個狀態(tài)機來管理整個過程。- 通過調(diào)用
GetEnumerator()
得到的 IEnumerator
對象就像是一個守護者,跟蹤當(dāng)前元素并帶你到下一個元素。 MoveNext()
是你前進的鑰匙,用于移動到下一個元素,而 Current
用于訪問當(dāng)前元素,告訴你當(dāng)前看到的是啥。foreach
循環(huán)在處理任何實現(xiàn)了 IEnumerable
接口的集合時,輕松自如。foreach
循環(huán)自動將代碼置入 try-finally 塊- 若類型實現(xiàn)了 IDisposable 接口,
foreach
循環(huán)會在循環(huán)結(jié)束后自動調(diào)用 Dispose 方法釋放
for 循環(huán)的內(nèi)部實現(xiàn)原理
for
循環(huán)是一種傳統(tǒng)的循環(huán)結(jié)構(gòu),使用上更為靈活,因為它允許你手動控制循環(huán)的開始、結(jié)束條件和迭代步長
它的內(nèi)部實現(xiàn)直接依賴于索引訪問或直接迭代,通過索引器來訪問每個元素。
for
循環(huán)的基本語法如下:
for (int i = 0; i < array.Length; i++)
{
// 訪問 array[i]
}
說明:以上這段代碼里的 i
是由程序員控制的索引變量,用于訪問數(shù)組或集合中的元素。
所以對于 for
循環(huán)的內(nèi)部實現(xiàn),我們可以簡單概括如下:
for
循環(huán)在編譯時會被轉(zhuǎn)換為一個簡單的計數(shù)器循環(huán),直接通過索引訪問數(shù)組或集合的元素- 對于數(shù)組來說,編譯器能夠直接計算出元素的地址,因此訪問速度非常快
- 在處理 List 或其他集合時,
for
循環(huán)依然能順暢使用索引,但要小心邊界檢查
兩者區(qū)別
經(jīng)過以上的探索,現(xiàn)在咱們來看看這兩哥們兒的區(qū)別:
foreach
循環(huán)自動管理迭代,你只要負(fù)責(zé)享受就好。for
循環(huán)需要你自己管理索引,要小心別越界!
foreach
循環(huán)語法優(yōu)雅簡潔,像是一首流暢的詩,讀起來讓人心情愉悅。for
循環(huán)的可讀性稍遜一籌,尤其是在多維數(shù)組里,就像是迷宮一樣。
foreach
循環(huán)對于實現(xiàn)了 IEnumerable
的集合可能會稍微慢一點。for
循環(huán)對于大數(shù)組的性能更好些。
foreach
循環(huán)在編譯時會檢查元素類型,就像是個嚴(yán)格的門衛(wèi)。for
循環(huán)可能會導(dǎo)致運行時類型轉(zhuǎn)換錯誤,就像是一本懸疑小說,有時驚險莫測。
foreach
不允許你在循環(huán)中對集合元素進行增刪操作,因為迭代器內(nèi)部維護了一個對集合版本的控制,任何對集合的增刪操作都會使用版本號加1,容易引發(fā)異常。for
可以在循環(huán)中對集合進行增刪操作,因為它直接使用索引器,它不對集合版本號進行判斷,所以不存在因為集合的變動而帶來的異常(當(dāng)然,超出索引長度這種情況除外)。
使用場景
- 程序只需要遍歷集合中的每個元素,而不需要增刪集合的元素時
- 集合實現(xiàn)了
IEnumerable
接口(如 List、Array、Dictionary 等)
- 程序需要通過索引訪問元素,比如在多維數(shù)組中
- 程序需要在遍歷過程中控制循環(huán)的步長,比如跳過某些元素
最終建議
- 如果你只需要遍歷集合,并且無需增刪集合元素,多數(shù)情況下
foreach
循環(huán)是首選 foreach
循環(huán)不能取代 for
循環(huán),當(dāng)你需要訪問索引或者需要在循環(huán)中增刪集合元素時,使用 for
循環(huán)- 對于程序健壯性要求比較高的程序,盡量使用
foreach
循環(huán),因為它是類型安全的,并且若類型實現(xiàn)了 IDisposable
接口,它會在循環(huán)結(jié)束后自動調(diào)用 Dispose
方法釋放資源 - 對于性能敏感的應(yīng)用程序,比如處理 10 萬條數(shù)據(jù)以上的大數(shù)組,可以考慮使用
for
循環(huán) - 在編寫代碼時,盡量保持代碼的可讀性和簡潔性,這樣可以減少出錯的機會
該文章在 2024/12/13 9:29:31 編輯過