日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Dotnet的局部函数和委托的对比

發(fā)布時(shí)間:2023/12/4 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Dotnet的局部函数和委托的对比 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

上一篇說了一下委托,這篇來說說局部函數(shù)和委托的對比。

?

把委托和局部函數(shù)放成前后篇,是因?yàn)檫@兩個(gè)內(nèi)容很像,用起來容易混。

需要了解委托相關(guān)內(nèi)容,可以看這一篇 【傳送門】

?

使用委托表達(dá)式(Lambda)

假設(shè)一個(gè)場景:我們有一個(gè)訂單列表,里面有售價(jià)和采購價(jià)。我們需要計(jì)算所有物品的毛利率。

public?class?OrderDetails {public?int?Id?{?get;?set;?}public?string?ItemName?{?get;?set;?}public?double?PurchasePrice?{?get;?set;?}public?double?SellingPrice?{?get;?set;?} }

通過迭代,我們可以計(jì)算出每個(gè)項(xiàng)目的毛利率:

static?void?Main(string[]?args) {List<OrderDetails>?lstOrderDetails?=?new?List<OrderDetails>();lstOrderDetails.Add(new?OrderDetails()?{?Id?=?1,?ItemName?=?"Item?1",?PurchasePrice?=?100,?SellingPrice?=?120?});lstOrderDetails.Add(new?OrderDetails()?{?Id?=?2,?ItemName?=?"Item?2",?PurchasePrice?=?800,?SellingPrice?=?1200?});lstOrderDetails.Add(new?OrderDetails()?{?Id?=?3,?ItemName?=?"Item?3",?PurchasePrice?=?150,?SellingPrice?=?150?});lstOrderDetails.Add(new?OrderDetails()?{?Id?=?4,?ItemName?=?"Item?4",?PurchasePrice?=?155,?SellingPrice?=?310?});lstOrderDetails.Add(new?OrderDetails()?{?Id?=?5,?ItemName?=?"Item?5",?PurchasePrice?=?500,?SellingPrice?=?550?});Func<double,?double,?double>?GetPercentageProfit?=?(purchasePrice,?sellPrice)?=>?(((sellPrice?-?purchasePrice)?/?purchasePrice)?*?100);foreach?(var?order?in?lstOrderDetails){Console.WriteLine($"Item?Name:?{order.ItemName},?Profit(%)?:?{GetPercentageProfit(order.PurchasePrice,?order.SellingPrice)}?");} }

例子中,我們創(chuàng)建了一個(gè)有5個(gè)商品的列表。我們還創(chuàng)建了一個(gè)委托表達(dá)式,并在循環(huán)中調(diào)用。

? ???

我們來看看這個(gè)委托表達(dá)式在IL中是什么樣子:

圖上能很清楚看到,Lambda被轉(zhuǎn)換成了類。

等等,為什么lambda表達(dá)式被轉(zhuǎn)成了類,而不是一個(gè)方法?

這里需要?jiǎng)澲攸c(diǎn)。Lambda表達(dá)式,在IL中會(huì)被轉(zhuǎn)為委托。而委托是一個(gè)類。關(guān)于委托為什么是一個(gè)類,可以去看上一篇。這兒知道結(jié)論就好。

所以,Lambda表達(dá)式會(huì)轉(zhuǎn)成一個(gè)類,應(yīng)該通過一個(gè)實(shí)例來使用。而這個(gè)實(shí)例是new出來的,所以是分配在堆上的。

另外,通過IL代碼我們也知道,IL是使用虛方法callvirt來調(diào)用的這個(gè)表達(dá)式。

?

現(xiàn)在,我們知道了一件事:Lambda會(huì)被轉(zhuǎn)成委托和類,由這個(gè)類的一個(gè)實(shí)例來使用。這個(gè)對象的生命周期必須由GC來處理。

使用局部函數(shù)(Local Function)

上面的示例代碼,我們換成局部函數(shù):

static?void?Main(string[]?args) {List<OrderDetails>?lstOrderDetails?=?new?List<OrderDetails>();lstOrderDetails.Add(new?OrderDetails()?{?Id?=?1,?ItemName?=?"Item?1",?PurchasePrice?=?100,?SellingPrice?=?120?});lstOrderDetails.Add(new?OrderDetails()?{?Id?=?2,?ItemName?=?"Item?2",?PurchasePrice?=?800,?SellingPrice?=?1200?});lstOrderDetails.Add(new?OrderDetails()?{?Id?=?3,?ItemName?=?"Item?3",?PurchasePrice?=?150,?SellingPrice?=?150?});lstOrderDetails.Add(new?OrderDetails()?{?Id?=?4,?ItemName?=?"Item?4",?PurchasePrice?=?155,?SellingPrice?=?310?});lstOrderDetails.Add(new?OrderDetails()?{?Id?=?5,?ItemName?=?"Item?5",?PurchasePrice?=?500,?SellingPrice?=?550?});double?GetPercentageProfit(double?purchasePrice,?double?sellPrice){return?(((sellPrice?-?purchasePrice)?/?purchasePrice)?*?100);}foreach?(var?order?in?lstOrderDetails){Console.WriteLine($"Item?Name:?{order.ItemName},?Profit(%)?:?{GetPercentageProfit(order.PurchasePrice,?order.SellingPrice)}?");} }

現(xiàn)在,我們在Main方法中放入了局部函數(shù)GetPercentageProfit。

我們再檢查下IL里的代碼:

沒有新類,沒有新對象,只是一個(gè)簡單的函數(shù)調(diào)用。

此外,Lambda表達(dá)式和局部函數(shù)的一個(gè)重要區(qū)別是IL中的調(diào)用方式。調(diào)用局部函數(shù)用call,它比callvirt要快,因?yàn)樗谴鎯?chǔ)在堆棧上的,而不是堆上。

通常我們不需要關(guān)注IL如何運(yùn)作,但好的開發(fā)人員真的需要了解一些框架的內(nèi)部細(xì)節(jié)。

call和callvert的區(qū)別在于,call不檢查調(diào)用者實(shí)例是否存在,而且callvert總是在調(diào)用時(shí)檢查,所以callvert不能調(diào)用靜態(tài)類方法,只能調(diào)用實(shí)例方法。

?

還是上面的例子,這回我們用迭代器實(shí)現(xiàn):

static?void?Main(string[]?args) {List<OrderDetails>?lstOrderDetails?=?new?List<OrderDetails>();lstOrderDetails.Add(new?OrderDetails()?{?Id?=?1,?ItemName?=?"Item?1",?PurchasePrice?=?100,?SellingPrice?=?120?});lstOrderDetails.Add(new?OrderDetails()?{?Id?=?2,?ItemName?=?"Item?2",?PurchasePrice?=?800,?SellingPrice?=?1200?});lstOrderDetails.Add(new?OrderDetails()?{?Id?=?3,?ItemName?=?"Item?3",?PurchasePrice?=?150,?SellingPrice?=?150?});lstOrderDetails.Add(new?OrderDetails()?{?Id?=?4,?ItemName?=?"Item?4",?PurchasePrice?=?155,?SellingPrice?=?310?});lstOrderDetails.Add(new?OrderDetails()?{?Id?=?5,?ItemName?=?"Item?5",?PurchasePrice?=?500,?SellingPrice?=?550?});var?result?=?GetItemSellingPice(lstOrderDetails);foreach?(string?s?in?result){Console.WriteLine(s.ToString());} }private?static?IEnumerable<string>?GetItemSellingPice(List<OrderDetails>?lstOrderDetails) {if?(lstOrderDetails?==?null)?throw?new?ArgumentNullException();foreach?(var?order?in?lstOrderDetails){yield?return?($"Item?Name:{order.ItemName},?Selling?Price:{order.SellingPrice}");} }

我們將列表傳遞給GetItemSellingPice。我們在方法中檢查了列表不能為null,并在循環(huán)中使用yield return返回?cái)?shù)據(jù)。

代碼看起來沒問題,是吧?

那我們假設(shè)列表真的為空,會(huì)怎么樣呢?應(yīng)該會(huì)返回ArgumentNullException,預(yù)期是這樣。

執(zhí)行一下看看,實(shí)際不是這樣。當(dāng)我們使用迭代器時(shí),方法并沒有立即執(zhí)行并返回異常,而是在我們使用結(jié)果foreach (string s in result)時(shí),才執(zhí)行并返回異常。這種情況,會(huì)讓我們對于異常的判斷和處理出現(xiàn)錯(cuò)誤。

這時(shí)候,局部函數(shù)就是一個(gè)好的解決方式:

static?void?Main(string[]?args) {var?result?=?GetItemSellingPice(null);foreach?(string?s?in?result){Console.WriteLine(s.ToString());} }private?static?IEnumerable<string>?GetItemSellingPice(List<OrderDetails>?lstOrderDetails) {if?(lstOrderDetails?==?null)?throw?new?ArgumentNullException();return?GetItemPrice();IEnumerable<string>?GetItemPrice(){foreach?(var?order?in?lstOrderDetails){yield?return?($"Item?Name:{order.ItemName},?Selling?Price:{order.SellingPrice}");}} }

現(xiàn)在,我們正確地在第一時(shí)間得到異常。

總結(jié)

局部函數(shù)是一個(gè)非常強(qiáng)大的存在。它與Lambda表達(dá)式類似,但有更優(yōu)的性能。

又是一個(gè)好東西,是吧?

總結(jié)

以上是生活随笔為你收集整理的Dotnet的局部函数和委托的对比的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。