LOGO OA教程 ERP教程 模切知識(shí)交流 PMS教程 CRM教程 開(kāi)發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

C#中容易被忽視的foreach

admin
2023年5月18日 15:19 本文熱度 704

有句俗語(yǔ):百姓日用而不知。我們c#程序員很喜歡,也非常習(xí)慣地用foreach。今天呢,我就帶大家一起探索foreach,走,開(kāi)始我們的旅程。

一、for語(yǔ)句用地好好的,為什么要提供一個(gè)foreach?

for (var i = 0; i < 10; i++) 
{    
//to do sth 
}
foreach (var n in list)
{    
//to do sth
}


首先,for循環(huán),需要知道循環(huán)的次數(shù),foreach不需要。其次,for循環(huán)在遍歷對(duì)象的時(shí)候,略顯麻煩,還需要通過(guò)下標(biāo)索引找到當(dāng)前對(duì)象,foreach不需要這么麻煩,顯得更優(yōu)雅。最后,for循環(huán)需要知道集合的細(xì)節(jié),foreach不需要知道。

這一切的好處,得益于微軟的封裝,那我們看看foreach生成的IL代碼:

IL_00a7:  callvirt   instance valuetype 
[System.Collections]System.Collections.Generic.List`1/Enumerator<!0>                        
class [System.Collections]System.Collections.Generic.List`1<int64>::GetEnumerator()
.try {   IL_00ae:  br.s       IL_00c9   IL_00b0:  ldloca.s   V_10   IL_00b2:  call       instance !0 valuetype [System.Collections]System.Collections.Generic.List`1/Enumerator<int64>::get_Current()
  IL_00cb:  call       instance bool valuetype [System.Collections]System.Collections.Generic.List`1/Enumerator<int64>::MoveNext()   IL_00d0:  brtrue.s   IL_00b0   IL_00d2:  leave.s    IL_00e3 }  // end .try finally {   IL_00d6:  constrained. valuetype [System.Collections]System.Collections.Generic.List`1/Enumerator<int64>   IL_00dc:  callvirt   instance void [System.Runtime]System.IDisposable::Dispose()   IL_00e1:  nop   IL_00e2:  endfinally }   // end handlers

怎樣的對(duì)象才能使用foreach呢?從微軟的文檔上看,實(shí)現(xiàn)了IEnumerable接口的對(duì)象,可以使用foreach,此接口只定義了一個(gè)方法:public System.Collections.IEnumerator GetEnumerator (); 有意思的是,它返回了一個(gè)IEnumerator接口,再看看這個(gè)接口:

有一個(gè)屬性:Current兩個(gè)方法MoveNext()、Reset(),現(xiàn)在我們回過(guò)頭來(lái)看看生成的IL代碼,真相大白。foreach只不過(guò)是個(gè)好吃的語(yǔ)法糖而已,編譯器幫我們做好了一切。和直接寫(xiě)foreach類似的用法還有一個(gè),就是對(duì)象的Foreach方法:

list.ForEach(n => { //to do sth }); 

那問(wèn)題就來(lái)了,都是foreach,我該用哪個(gè)?

忍不住看看微軟的源碼:

internal void ForEach(Action<T> action)
{    
foreach (T x in this)    
{       
action(x);    
} 
} 

其實(shí),就是定義了一個(gè)委托,我們把想要做的事情定義好,它來(lái)執(zhí)行。這和直接使用foreach有何區(qū)別?我又忍不住好奇心,寫(xiě)了一段代碼,比較了for和foreach的性能,先上結(jié)果:

說(shuō)明下,最后一個(gè)是對(duì)象調(diào)用Foreach方法。數(shù)據(jù)反映的是隨著數(shù)據(jù)規(guī)模下降,看運(yùn)行時(shí)間有什么變化。從1億次循環(huán)到1萬(wàn)次循環(huán),耗時(shí)從幾百毫秒到1毫秒以內(nèi)。從圖上,明顯能看出性能差異,是從千萬(wàn)級(jí)別開(kāi)始,for的性能最好,其次是對(duì)象的Foreach方法,最后是foreach。

for和foreach的性能差異,我們尚且能理解,但是對(duì)象的Foreach和直接foreach差異從何而來(lái)?我冥思苦想,百思不得其解。我試圖從內(nèi)存分配和垃圾回收的機(jī)制方向去理解,但是沒(méi)有突破。我想著,直接foreach耗時(shí),是不是因?yàn)椋鄨?zhí)行了什么東西,比如說(shuō)多分配了一些變量,比如說(shuō),內(nèi)存中這么大數(shù)據(jù)量,垃圾回收機(jī)制,不可能無(wú)動(dòng)于衷,是不是垃圾回收機(jī)制導(dǎo)致的程序變慢,進(jìn)而影響了性能。

我在循環(huán)完后,強(qiáng)行執(zhí)行了一次GC,才釋放了13.671875k,說(shuō)明循環(huán)中,執(zhí)行GC也沒(méi)有什么意義,回收不了垃圾,但是如果循環(huán)中,頻繁執(zhí)行GC,確實(shí)會(huì)導(dǎo)致程序沒(méi)法好好地運(yùn)行。垃圾回收機(jī)制,會(huì)把不再引用的對(duì)象釋放,而整個(gè)循環(huán)過(guò)程中,對(duì)象都在List中,所以GC應(yīng)該不會(huì)運(yùn)行。

那親愛(ài)的程序員朋友,你覺(jué)得對(duì)象的Foreach方法和直接Foreach的性能差異,是怎么產(chǎn)生的呢,歡迎討論,我把源碼貼出來(lái)。

using System;using System.Collections.Generic;using System.Diagnostics;using System.Text;
namespace MyConsole.Test{    public class ForeachTest    {        public static void Test(long num)        {            Console.WriteLine("當(dāng)前數(shù)據(jù)規(guī)模:" + num);
           DateTime start = DateTime.Now;
           for (var i = 0; i < num; i++)            {                var t = (i + 1) * 100 + 1;            }
           DateTime end = DateTime.Now;
           var costTime = end.Subtract(start).TotalMilliseconds;
           Console.WriteLine("for cost time:" + costTime + " ms");

           List<long> list = new List<long>();            for (var i = 0; i < num; i++)            {                list.Add(i);            }
           start = DateTime.Now;
           foreach (var n in list)            {                var t = (n + 1) * 100 + 1;            }
           end = DateTime.Now;
           costTime = end.Subtract(start).TotalMilliseconds;
           Console.WriteLine("foreach cost time:" + costTime + " ms");

           start = DateTime.Now;
           list.ForEach(n =>            {                var t = (n + 1) * 100 + 1;            });
           end = DateTime.Now;
           costTime = end.Subtract(start).TotalMilliseconds;
           Console.WriteLine("obj foreach cost time:" + costTime + " ms");
           Console.WriteLine("--------------------------------------------");            Console.WriteLine("");        }    }}

放到Main方法里:

long[] nums = {     100000000,     10000000,     1000000,     100000,     10000, };
foreach (int num in nums) {     for (int i = 0; i < 5; i++)     {         ForeachTest.Test(num);     } } Console.ReadLine();
 最后注意一點(diǎn)的是,foreach循環(huán)里面,不能隨便添加或者刪除元素,如果允許的話,程序?qū)⒑茈y控制,而且非常容易出錯(cuò),所以微軟不允許這么干。

該文章在 2023/5/18 15:19:27 編輯過(guò)
關(guān)鍵字查詢
相關(guān)文章
正在查詢...
點(diǎn)晴ERP是一款針對(duì)中小制造業(yè)的專業(yè)生產(chǎn)管理軟件系統(tǒng),系統(tǒng)成熟度和易用性得到了國(guó)內(nèi)大量中小企業(yè)的青睞。
點(diǎn)晴PMS碼頭管理系統(tǒng)主要針對(duì)港口碼頭集裝箱與散貨日常運(yùn)作、調(diào)度、堆場(chǎng)、車隊(duì)、財(cái)務(wù)費(fèi)用、相關(guān)報(bào)表等業(yè)務(wù)管理,結(jié)合碼頭的業(yè)務(wù)特點(diǎn),圍繞調(diào)度、堆場(chǎng)作業(yè)而開(kāi)發(fā)的。集技術(shù)的先進(jìn)性、管理的有效性于一體,是物流碼頭及其他港口類企業(yè)的高效ERP管理信息系統(tǒng)。
點(diǎn)晴WMS倉(cāng)儲(chǔ)管理系統(tǒng)提供了貨物產(chǎn)品管理,銷售管理,采購(gòu)管理,倉(cāng)儲(chǔ)管理,倉(cāng)庫(kù)管理,保質(zhì)期管理,貨位管理,庫(kù)位管理,生產(chǎn)管理,WMS管理系統(tǒng),標(biāo)簽打印,條形碼,二維碼管理,批號(hào)管理軟件。
點(diǎn)晴免費(fèi)OA是一款軟件和通用服務(wù)都免費(fèi),不限功能、不限時(shí)間、不限用戶的免費(fèi)OA協(xié)同辦公管理系統(tǒng)。
Copyright 2010-2025 ClickSun All Rights Reserved

黄频国产免费高清视频,久久不卡精品中文字幕一区,激情五月天AV电影在线观看,欧美国产韩国日本一区二区
亚洲一区二区三区乱码AⅤ 在线播放国产日韩 | 久久99精品久久不卡 | 午夜福利视频-92 | 在线播放一区二区不卡三区 | 亚洲午夜久久久久久91 | 思思久久精品免费视频 |