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

上一篇说了一下委托,这篇来说说局部函数和委托的对比。

把委托和局部函数放成前后篇,是因为这两个内容很像,用起来容易混。

需要了解委托相关内容,可以看这一篇 【传送门】

使用委托表达式(Lambda)

假设一个场景:我们有一个订单列表,里面有售价和采购价。我们需要计算所有物品的毛利率。

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

通过迭代,我们可以计算出每个项目的毛利率:

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)} ");}
}

例子中,我们创建了一个有5个商品的列表。我们还创建了一个委托表达式,并在循环中调用。

     

我们来看看这个委托表达式在IL中是什么样子:

图上能很清楚看到,Lambda被转换成了类。

等等,为什么lambda表达式被转成了类,而不是一个方法?

这里需要划重点。Lambda表达式,在IL中会被转为委托。而委托是一个类。关于委托为什么是一个类,可以去看上一篇。这儿知道结论就好。

所以,Lambda表达式会转成一个类,应该通过一个实例来使用。而这个实例是new出来的,所以是分配在堆上的。

另外,通过IL代码我们也知道,IL是使用虚方法callvirt来调用的这个表达式。

现在,我们知道了一件事:Lambda会被转成委托和类,由这个类的一个实例来使用。这个对象的生命周期必须由GC来处理。

使用局部函数(Local Function)

上面的示例代码,我们换成局部函数:

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)} ");}
}

现在,我们在Main方法中放入了局部函数GetPercentageProfit

我们再检查下IL里的代码:

没有新类,没有新对象,只是一个简单的函数调用。

此外,Lambda表达式和局部函数的一个重要区别是IL中的调用方式。调用局部函数用call,它比callvirt要快,因为它是存储在堆栈上的,而不是堆上。

通常我们不需要关注IL如何运作,但好的开发人员真的需要了解一些框架的内部细节。

callcallvert的区别在于,call不检查调用者实例是否存在,而且callvert总是在调用时检查,所以callvert不能调用静态类方法,只能调用实例方法。

还是上面的例子,这回我们用迭代器实现:

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,并在循环中使用yield return返回数据。

代码看起来没问题,是吧?

那我们假设列表真的为空,会怎么样呢?应该会返回ArgumentNullException,预期是这样。

执行一下看看,实际不是这样。当我们使用迭代器时,方法并没有立即执行并返回异常,而是在我们使用结果foreach (string s in result)时,才执行并返回异常。这种情况,会让我们对于异常的判断和处理出现错误。

这时候,局部函数就是一个好的解决方式:

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}");}}
}

现在,我们正确地在第一时间得到异常。

总结

局部函数是一个非常强大的存在。它与Lambda表达式类似,但有更优的性能。

又是一个好东西,是吧?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/305171.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

经纬度 c代码中定义_如何将TXT文本格式的批量经纬度值导入到奥维成为标签

文本编辑&#xff1a;示例1&#xff1a;最基本的&#xff0c;只批量导入WGS-84经纬度值成为标签&#xff0c;不需要导入标签名称。 文本编辑格式&#xff1a;经度值空格纬度值换行&#xff0c;如下图&#xff1a;示例2&#xff1a;除WGS-84经纬度外&#xff0c;还要导入标签名称…

中国式创新技术“步态识别”终于来临,你大胆地走两步,我就知道你是谁

放完假的数据君&#xff0c;回到办公室&#xff0c;苦恼该码一篇什么文章&#xff0c;来给各位送上“节后的祝福”。 这么想着&#xff0c;数据君便开始浏览最新的科技报道&#xff1a; 什么鬼&#xff01;这难道是什么新兴的黑科技吗&#xff1f;&#xff01; 数据君赶紧查了…

帆软获取上月的第一天与最后一天_《原神》岩港打工第一天怎么玩 岩港打工第一天玩法攻略...

《原神》在11月2日开启了岩港奇珍行记&#xff0c;玩家可以在璃月港进行打工了&#xff0c;可能有的小伙伴还不清楚第一天的打工要怎么做&#xff0c;所以小编这次就为大家带来了《原神》岩港打工第一天玩法攻略&#xff0c;感兴趣的小伙伴可以来看一下。岩港打工第一天玩法攻略…

mysql卸载时弹框,win10卸载mysql5安装mysql8

使用mysql5的过程中使用 datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP 无法执行&#xff0c;原因是版本问题&#xff0c;因此我需要安装更高级的版本。一、卸载原有的mysql1、在控制面板中卸载mysql2、运行“regedit”文件&#xff0c;删除HKEY_LOCAL_MACHINE\SYSTEM\Co…

感谢Adobe,用上了Silverlight RC0版本

Adobe 23号发布CS4系列&#xff0c;24号网络上出现下载&#xff0c; MicorSoft 25号发布Silverlight RC0&#xff0c;26号网络上出现下载。 用上了Silverlight RC0&#xff0c; 真的要感谢Adobe&#xff0c;推出全新体验的Adobe Flash CS4正式版&#xff0c;也是Flash 10&#…

css中的单位换算_金蝶ERP入门教程:动态换算率及辅助计量单位的应用

金蝶ERP入门教程&#xff1a;动态换算率及辅助计量单位的应用关注我&#xff0c;我将定期分享更多的ERP解决方案如果您喜欢且觉得内容有用&#xff0c;请点击分享转发如果你有什么关于ERP系统的问题和疑问&#xff0c;可私信联系我大家好&#xff0c;我是Eric顾问哥&#xff1a…

因为加班,谈了7年的女友跟我分手了……

记得有一句很流行的话&#xff1a; 世界那么大&#xff0c;我想去看看 此时的你&#xff0c;走到了哪里&#xff1f; 还是停留在这里吗&#xff1f; 世界很小&#xff0c;小到仅有这几尺办公桌 工作很多&#xff0c;多到生活只余下工作 最近&#xff0c;腾讯上线了一支视频广告…

cf鼠标宏数据大全_游戏鼠标中的跑车,贱驴125M游戏鼠标体验!

如今全民玩游戏的年代&#xff0c;每个年轻人对自己玩游戏的装备也越来越看重。也越来越喜欢在这方面花钱&#xff0c;因为高手过招输赢本就在毫厘之间&#xff0c;如果能够拥有一款性能更强&#xff0c;使用更顺手的游戏装备&#xff0c;在玩游戏竞赛中&#xff0c;无疑有多了…

如何使用 Entity Framework 的 DbContext

微软的 Entity Framework 是一个开源的 对象-关系映射 ORM 框架&#xff0c;它帮助我们打通了 数据库的数据模型 到 代码层的领域模型&#xff0c;Entity Framework 简化了应用程序对数据库的 CURD 操作&#xff0c;而且还向高层屏蔽了数据是如何持久化到数据库的。说的具体一点…

php绘制饼图,php怎么绘制饼图?

php怎么绘制饼图&#xff1f;在php中&#xff0c;可以使用GD绘制饼图。GD库是php处理图形的扩展库&#xff0c;GD库提供了一系列用来处理图片的API&#xff0c;使用GD库可以处理图片&#xff0c;或者生成图片&#xff0c;也可以给图片加水印。PHP中用GD绘制饼图&#xff0c;绘制…

silklabo哪个公众号有资源_微小说免费渣渣团资源公众号看大全集

“免费小说全本”公众号。小说免费阅读吧|免费小说在线阅读网|免费小说资源|小说传送站&#xff5c;小说免费公众号推荐&#xff5c;免费小说全集书城推荐&#xff5c;免费小说公众号渣渣团&#xff5c;小说免费阅读叶辰萧初然|霸道总裁&#xff5c;先生是谁等全免费&#xff5…

大数据分析苏轼,你没看错,这些都是小学生完成的

适逢苏轼先生诞辰980周年&#xff0c;清华附小开展了一系列致敬苏轼的活动。同学们利用假期时间共完成课题研究报告23份&#xff1a;《大数据帮你进一步认识苏轼》、《苏轼的朋友圈》、《苏轼的旅游品牌价值分析》、《苏轼vs李白》等。是的&#xff0c;你没看错&#xff0c;这些…

深度解读.NET 5授权中间件的执行策略

前文提要2021.1月份我写了一个《这难道不是.NET5 的bug? 在线求锤&#xff1f;》&#xff0c;讲述了我在实现[全局授权访问特例匿名访问] 遇到的技术困惑: [特例匿名访问&#xff0c;怎么走了认证流程&#xff1f;]。博客园上某大佬的看法&#xff1a;大概的意思是说 &…

噪声与振动控制工程手册_仨亿技术丨工程机械噪声与控制分析

随着我国经济的发展&#xff0c;各种工程规模不断扩大、数量不断增多&#xff0c;这些工程在造福于人们并促进我国综合国力提高的同时&#xff0c;也在一定程度上带来一些工程机械噪声的问题。工程机械噪声的存在&#xff0c;不仅给工程机械的使用寿命带来不利影响&#xff0c;…

EFCore查缺补漏(一):依赖注入

前段时间&#xff0c;在群里潜水的时候&#xff0c;看见有个群友的报错日志是这样的&#xff1a;An unhandled exception was thrown by the application. System.OutOfMemoryException: Exception of type System.OutOfMemoryException was thrown.at System.Threading.Thread…

sql倒序排列取第一条_从零学会SQL·三——汇总分析

一、常用汇总函数的练习score表问题&#xff1a;查询课程编号为“0002”的总成绩查询选了课程的学生人数查询学号为“0001”的学生的最高分、最低分和平均分解答SQL语句如下&#xff1a;-- 查询课程编号为“0002”的总成绩 SELECT 学号,SUM(成绩)FROM score WHERE 课程号0002;-…

2017 软件开发薪酬调查:Go 和 Scala 是最赚钱的语言

英文&#xff1a;codeburst&#xff0c;编译&#xff1a;oschina www.oschina.net/news/87499/go-scala-are-the-most-lucrative-languages 每年 O’Reilly 都会发布其年度开发者的调查结果。这项调查涵盖了来自世界 110 个国家的近 7000 名程序员。这里摘几个亮点&#xff1a;…

bootstrap grid php,bootstrap grid用法

bootstrap grid的用法&#xff1a;首先使用container来包裹div&#xff1b;然后在div里面设置行&#xff1b;接着设置列能够快速对这个框架进行搭建&#xff1b;最后通过拖拽浏览器来改变宽窄即可。本文操作环境&#xff1a;Windows7系统、bootstrap3、Dell G3电脑。bootstrap的…

js map对象遍历_前端测试题:有关于js中跨域请求的说法,错误的是?

考核内容:javascript 跨域的使用题发散度: ★试题难度: ★解题思路:什么是跨域&#xff1f;跨域是指一个域下的文档或脚本试图去请求另一个域下的资源&#xff0c;这里跨域是广义的。什么是同源策略&#xff1f;同源策略/SOP(Same origin policy)是一种约定&#xff0c;由Netsc…

管理信息系统案例分析_「案例」MES系统助力华联电子仓库工作人员效率提高30%...

一、企业简介厦门华联电子股份有限公司成立于1984年&#xff0c;注册资金12929万元&#xff0c;现有自主厂房8万平方米&#xff0c;全自动化生产线40多条&#xff0c;员工2400余人&#xff0c;是国内最具规模的智能控制器、光电子器件的生产厂家之一。企业集研发、生产、销售和…