提起函数式编程,大家一定想到的是语法高度灵活和动态的LISP,Haskell这样古老的函数式语言,往近了说ruby,javascript,F#也是函数式编程的流行语言。然而自从.net支持了lambda表达式,C#虽然作为一种指令式程序设计语言,在函数式编程方面也毫不逊色。我们在使用c#编写代码的过程中,有意无意的都会使用高阶函数,组合函数,纯函数缓存等思想,连表达式树这样的idea也来自函数式编程思想。所以接下来我们把常用的函数式编程场景做个总结,有利于我们在程序设计过程中灵活应用这些技术,拓展我们的设计思路和提高代码质量。
一、高阶函数
高阶函数通俗的来讲:某个函数中使用了函数作为参数,这样的函数就称为高阶函数。根据这样的定义,.net中大量使用的LINQ表达式,Where,Select,SelectMany,First等方法都属于高阶函数,那么我们在自己写代码的时候什么时候会用到这种设计?
举例:设计一个计算物业费的函数,var fee=square*price, 而面积(square)根据物业性质的不同,计算方式也不同。民用住宅,商业住宅等需要乘以不同的系数,根据这样的需求我们试着设计下面的函数:
民用住宅面积:
public Func<int,int,decimal> SquareForCivil() { return (width,hight)=>width*hight; }
商业住宅面积:
public Func<int, int, decimal> SquareForBusiness() { return (width, hight) => width * hight*1.2m; }
这些函数都有共同的签名:Func31803993a3bae4f7d54758d225c3e107,所以我们可以利用这个函数签名设计出计算物业费的函数:
public decimal PropertyFee(decimal price,int width,int hight, Func<int, int, decimal> square) { return price*square(width, hight); }
是不是很easy,写个测试看看
[Test] public void Should_calculate_propertyFee_for_two_area() { //Arrange var calculator = new PropertyFeeCalculator(); //Act var feeForBusiness= calculator.PropertyFee(2m,2, 2, calculator.SquareForBusiness()); var feeForCivil = calculator.PropertyFee(1m, 2, 2, calculator.SquareForCivil()); //Assert feeForBusiness.Should().Be(9.6m); feeForCivil.Should().Be(4m); }
二、惰性求值
C#在执行过程使用严格求值策略,所谓严格求值是指参数在传递给函数之前求值。这个解释是不是还是有点不够清楚?我们看个场景:有一个任务需要执行,要求当前内存使用率小于80%,并且上一步计算的结果46c3f0744c8dff3b1bcb61e3393fa07f类,大家可以在有这种需求的场景下使用这个机制。
三、函数柯里化(Curry)
柯里化也称作局部套用。定义:是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术,ps:为什么官方解释这么绕口?
看到这样的定义估计大家也很难明白这是这么一回事,所以我们从curry的原理讲起:
写一个两个数相加的函数:
public Func<int, int, int> AddTwoNumber() { return (x, y) => x + y; }
ok, 如何使用这个函数?
var result= _curringReasoning.AddTwoNumber()(1,2);
1+2=3,调用很简单。需求升级,我们需要一个函数,这个函数要求输入一个参数(number),算出10+输入的参数(number)的结果。估计有人要说了,这需求上面的代码完全可以实现啊,第一个参数你传入10不就完了么,ok,如果你是这样想的,我也是无可奈何。还有人可能说了,再写一个重载,只要一个参数即可,实际情况是不容许,我们在调用别人提供的api,无法添加重载。可以看到局部套用的使用场景不是一种很普遍的场景,所以在合适的场景配合合适的技术才是最好的设计,我们来看局部套用的实现:
public Func<int, Func<int, int>> AddTwoNumberCurrying() { Func<int, Func<int, int>> addCurrying = x => y => x + y; return addCurrying; }
表达式x => y => x + y得到的函数签名为Func9d41917661ce24c26c6c064bdf051b81>,这个函数签名非常清楚,接收一个int类型的参数,得到一个Func6721ea9c2f3961346cef19f5e7477758类型的函数。此时如果我们再调用:
//Act var curringResult = curringReasoning.AddTwoNumberCurrying()(10); var result = curringResult(2); //Assert result.Should().Be(12);
这句话:var curringResult = curringReasoning.AddTwoNumberCurrying()(10); 生成的函数就是只接收一个参数(number),且可以计算出10+number的函数。
同样的道理,三个数相加的函数:
public Func<int,int,int,int> AddThreeNumber() { return (x, y, z) => x + y + z; }
局部套用版本:
public Func<int,Func<int,Func<int,int>>> AddThreeNumberCurrying() { Func<int, Func<int, Func<int, int>>> addCurring = x => y => z => x + y + z; return addCurring; }
调用过程:
[Test] public void Three_number_add_test() { //Arrange var curringReasoning = new CurryingReasoning(); //Act var result1 = curringReasoning.AddThreeNumber()(1, 2, 3); var curringResult = curringReasoning.AddThreeNumberCurrying()(1); var curringResult2 = curringResult(2); var result2 = curringResult2(3); //Assert result1.Should().Be(6); result2.Should().Be(6); }
当函数参数多了之后,手动局部套用越来越不容易写,我们可以利用扩展方法自动局部套用:
public static Func<T1, Func<T2, TResult>> Curry<T1, T2, TResult>(this Func<T1, T2, TResult> func) { return x => y => func(x, y); } public static Func<T1, Func<T2, Func<T3, TResult>>> Curry<T1, T2, T3, TResult>(this Func<T1, T2, T3,TResult> func) { return x => y => z=>func(x, y,z); }
同样的道理,Actiona8093152e673feb7aba1828c43532094签名的函数也可以自动套用
有了这些扩展方法,使用局部套用的时候就更加easy了
[Test] public void Should_auto_curry_two_number_add_function() { //Arrange var add = _curringReasoning.AddTwoNumber(); var addCurrying = add.Curry(); //Act var result = addCurrying(1)(2); //Assert result.Should().Be(3); }
好了,局部套用就说到这里,stackoverflow有几篇关于currying使用的场景和定义的文章,大家可以继续了解。
函数式编程还有一些重要的思想,例如:纯函数的缓存,所为纯函数是指函数的调用不受外界的影响,相同的参数调用得到的值始终是相同的。尾递归,单子,代码即数据(.net中的表达式树),部分应用,组合函数,这些思想有的我也仍然在学习中,有的还在思考其最佳使用场景,所以不再总结,如果哪天领会了其思想会补充。
四、设计案例
最后我还是想设计一个场景,把高阶函数,lambda表达式,泛型方法结合在一起,我之所以设计这样的例子是因为现在很多的框架,开源的项目都有类似的写法,也正是因为各种技术和思想结合在一起,才有了极富有表达力并且非常优雅的代码。
需求:设计一个单词查找器,该查找器可以查找某个传入的model的某些字段是否包含某个单词,由于不同的model具有不同的字段,所以该查找需要配置,并且可以充分利用vs的智能提示。
这个功能其实就两个方法:
private readonly List<Func<string, bool>> _conditions; public WordFinder<TModel> Find<TProperty>(Func<TModel,TProperty> expression) { Func<string, bool> searchCondition = word => expression(_model).ToString().Split(' ').Contains(word); _conditions.Add(searchCondition); return this; } public bool Execute(string wordList) { return _conditions.Any(x=>x(wordList)); }
使用:
[Test] public void Should_find_a_word() { //Arrange var article = new Article() { Title = "this is a title", Content = "this is content", Comment = "this is comment", Author = "this is author" }; //Act var result = Finder.For(article) .Find(x => x.Title) .Find(x => x.Content) .Find(x => x.Comment) .Find(x => x.Author) .Execute( "content"); //Assert result.Should().Be(true); }
该案例本身不具有实用性,但是大家可以看到,正是各种技术的综合应用才设计出极具语义的api, 如果函数参数改为Expression7e21a607b5f8b4a44e84d6cad53bd045> 类型,我们还可以读取到具体的属性名称等信息。
以上是详细介绍C#函数式编程的示例代码的详细内容。更多信息请关注PHP中文网其他相关文章!

C#.NET开发的最新动态和最佳实践包括:1.异步编程提高应用响应性,使用async和await关键字简化非阻塞代码;2.LINQ提供强大查询功能,通过延迟执行和表达式树高效操作数据;3.性能优化建议包括使用异步编程、优化LINQ查询、合理管理内存、提升代码可读性和维护性、以及编写单元测试。

如何利用.NET构建应用?使用.NET构建应用可以通过以下步骤实现:1)了解.NET基础知识,包括C#语言和跨平台开发支持;2)学习核心概念,如.NET生态系统的组件和工作原理;3)掌握基本和高级用法,从简单控制台应用到复杂的WebAPI和数据库操作;4)熟悉常见错误与调试技巧,如配置和数据库连接问题;5)应用性能优化与最佳实践,如异步编程和缓存。

C#在企业级应用、游戏开发、移动应用和Web开发中均有广泛应用。1)在企业级应用中,C#常用于ASP.NETCore开发WebAPI。2)在游戏开发中,C#与Unity引擎结合,实现角色控制等功能。3)C#支持多态性和异步编程,提高代码灵活性和应用性能。

C#和.NET适用于Web、桌面和移动开发。1)在Web开发中,ASP.NETCore支持跨平台开发。2)桌面开发使用WPF和WinForms,适用于不同需求。3)移动开发通过Xamarin实现跨平台应用。

C#.NET生态系统提供了丰富的框架和库,帮助开发者高效构建应用。1.ASP.NETCore用于构建高性能Web应用,2.EntityFrameworkCore用于数据库操作。通过理解这些工具的使用和最佳实践,开发者可以提高应用的质量和性能。

如何将C#.NET应用部署到Azure或AWS?答案是使用AzureAppService和AWSElasticBeanstalk。1.在Azure上,使用AzureAppService和AzurePipelines自动化部署。2.在AWS上,使用AmazonElasticBeanstalk和AWSLambda实现部署和无服务器计算。

C#和.NET的结合为开发者提供了强大的编程环境。1)C#支持多态性和异步编程,2).NET提供跨平台能力和并发处理机制,这使得它们在桌面、Web和移动应用开发中广泛应用。

.NETFramework是一个软件框架,C#是一种编程语言。1..NETFramework提供库和服务,支持桌面、Web和移动应用开发。2.C#设计用于.NETFramework,支持现代编程功能。3..NETFramework通过CLR管理代码执行,C#代码编译成IL后由CLR运行。4.使用.NETFramework可快速开发应用,C#提供如LINQ的高级功能。5.常见错误包括类型转换和异步编程死锁,调试需用VisualStudio工具。


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

EditPlus 中文破解版
体积小,语法高亮,不支持代码提示功能

记事本++7.3.1
好用且免费的代码编辑器

禅工作室 13.0.1
功能强大的PHP集成开发环境

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

Atom编辑器mac版下载
最流行的的开源编辑器