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