C#函数式程序设计之函数、委托和Lambda表达式
C#函数式程序设计之函数、委托和Lambda表达式
C#函数式程序设计之函数、委托和Lambda表达式
相信很多人都听说过函数式编程,提到函数式程序设计,脑海里涌现出来更多的是Lisp、Haskell等语言,而C#,似乎我们并不把它当成函数式语言,其实,函数式程序设计并不是只针对某个特定的程序设计语言,而C#,也正一步步使用函数式丰富自己的语言结构,帮助人们更好的实现期望的结果。
函数式程序设计
函数式程序设计把重点放在函数的应用上,函数式程序设计人员以函数为基本模块来建立新函数,这并不是说没有其他语言的成分,而是说函数是程序体系创建的主要构造。
引用透明(Referential transparency)是函数式程序设计领域中的一个重要思想。一个引用透明的函数的返回值只取决于传递给它的参数的值。这正好与指令程序设计的基本思想相反。在指令程序设计中,程序的状态通常会影响函数的返回值。引用透明的函数的数学意义仅存在于函数式程序设计中,这样的函数称为纯函数,没有副作用。
函数式程序设计属于一种定向思维。如果我们愿意按某种方式去思考,则它可以给我们提供有趣的解决方案或者至少思考的源头,它们都与当前程序设计的许多实际问题有关。
C#无法做到像Lisp、Haskell或同属于.NET平台的F#那样很容易实现函数式程序设计,这点我们必须承认,但从各方面来讲,用C#实现函数式程序设计确实是有意义的。
C#函数式程序设计基础之函数与方法
由于C#的函数只能出现在类中,因此它们通常被称为方法。方法可以接受若干个参数,并且可以有一个返回值。
与许多面向对象语言一样,C#类中的方法可以是实例方法,也可以是类方法。而在纯函数式程序设计中,没有类,也没有类的实例——当然,有很多方法保存数据,但通常不是用类来保存数据,它们总是在许多方面表现出不同。
在面向对象环境中,所有其他元素只能出现在类和对象的内部(对象是类实例的另一个说法);而在函数式程序设计中,所有其他元素都出现在函数内部。有些数据保存在函数的局部变量中,就像C#那样定义在方法内部的变量,但这并不是保存数据最理想的方法。
F#把类级别的成员当成全局成员,同时由于得到特殊语法的支持,程序员不需要考虑实际发生的“转换”过程,遗憾的是,在C#中无法实现这一点,但是解决方法是一样的。
为了调用全局级的函数(或者任何其他作用域的函数),必须在类内创建类级别的成员。这些成员要用static关键字。由于它们都封装在类中,因此类中的成员有不同的可见度。大多数函数式设计环境都有不同的封装级别——如模块级或命名空间级——因此除了C#中一些比较复杂的语法外,实际上两者没有多大的区别。
有些函数式语言使用顶级函数或者允许导入模块或命名空间,这样就不需要函数调用的修饰符:
|
1
|
DoSomething "string paramers" |
在C#中,这样的调用总是需要一个修饰符,即类名,除非这个函数出现在同一个类的内部:
|
1
|
SomeClass.DoSomething("string paramers"); |
C#函数式程序设计基础之重用函数
在计算机程序设计中,重用是一个非常重要的综合问题。函数并不是可重用性的唯一方法,特别在面向对象程序设计中,很快出现了其他方法。作为C#的一个内置功能,它只支持函数的重载作为函数级模块化的直接办法,C#4.0支持命名参数和可选参数,因此重载函数的解析过程变得相当复杂,特别当它与其他相关方法(如在方法调用时进行泛型类型推断)一起使用时。
下面举一个重载方法的简单例子:
在这个例子中,我们很清楚地看出为什么重载与重用有关:它允许程序员创建与原函数类似的新函数,同时尽可能利用原函数已有的功能。
C#函数式程序设计基础之匿名函数与Lambda表达式
并非所有的函数都重要到需要一个名称,一般而言,这些函数并不是类级别的函数,它们没有名称,这些函数的引用地址保存在变量中,因此只要有这些函数的引用地址就可以调用它们。
从技术上讲,匿名函数肯定要受到某些限制。很遗憾的是,其中之一就是它们不可以是泛型,它们也不可以用来实现迭代器。除此之外,匿名函数几乎可以包括所有做任何“正常”方法可以做的事情。
以上是C#2.0的代码,可以看出,关键字delegate委托代替了方法名。参数列表和方法体还是与前面一样。这个匿名方法也可以改写成如下形式,这里用了C#3.0的Lambda表达式语法:
这段代码较短,因为少了delegate关键字,方法体已经写成一行格式。Lambda表达式中的主体=>运算符右侧的部分。可以采取若干方法进一步简化代码。首先, 可以省略参数类型,因为编译器可以根据委托类型的声明语句推断出参数的类型:
其次,由于函数除了返回一个值外不执行任何操作,因此可以把函数体转换为表达式体,并且可以利用隐式返回:
表达式体很有用。因为有了它,在函数式程序中本来需要用函数实现的某个操作现在可以简化为一个表达式。与函数一样,表达式体也要接受参数并返回一个值。表达式体不可以包含任何与返回值求值无关的代码(即只要有一个返回值就行,遗憾的是,经常在表达式体中使用没有返回值的表达式)。
前面的例子如果使用其中一个泛型委托类型,就可以变成如下的形式:
这个委托需要接受两个object类型的参数,返回一个bool值。使用泛型委托类型的另一个好处是,它们的参数类型更容易看明白,因为它们在委托类型中采用显式声明,而且编译器可以为Lambda表达式推断出它们的类型。
使用Lambda表达式时,有一个细节需要牢记:只有当所有类型都确定后,编译器才会根据几个比较复杂的准则进行类型推断。编译器并不是总能正确地推断出类型,因此,如果所有的类型都确定了,编译器的要求就满足了:
在这个Lambda表达式中不可以使用var关键字,C#中,编译器必须能够在声明的位置推断出参数的类型,对于下面的语句则无法推断出参数的类型:
函数式程序设计语言要求,在所有与类型推断有关的情形中都需要像这样的显式说明。这在某些C#程序员看来是遗憾的。
C#函数式程序设计基础之扩展方法
扩展方法是静态类中用特殊方法表示的静态方法:
表示Concat是一个扩展方法的标志是在该方法的参数列表中使用this关键字。这个关键字是C#专有的,用于命令编译器给这个方法中增加ExtensionMethodAttribute属性。可以像调用静态方法那样调用扩展方法:
然而,由于它是扩展方法,因此也可以像下面这样调用:
当我们需要充分利用扩展方法的优点时,这种调用方法比较简单。
每个扩展方法都有一个可扩展的特定类型:第一个参数的类型,即用this标志的那个参数。这个标志只可以用于第一个参数,不可以用于其他参数。扩展方法的第一个参数可以是一个基类类型或者一个接口,甚至可以是System.Object中的对象。扩展方法也可以是泛型的,他们可以扩展泛型类型。
C#函数式程序设计基础之引用透明
在指令式程序设计中,这些模块的基本作用是防止代码重复,把代码分解成更容易管理的函数级模块。指令式程序设计的最大问题之一是随着时间的推移,模块会变得越来越大。由于指令式程序设计把重点放在执行序列上,因此函数和方法的引用总是不透明的。
引用透明:表达式可以用表达式的值取代而不会影响程序,也就是不会影响使用此替换操作的算法的最终结果。
C#函数式程序设计之函数、委托和Lambda表达式的更多相关文章
- 转载 C#匿名函数 委托和Lambda表达式
转载原出处: http://blog.csdn.net/honantic/article/details/46331875 匿名函数 匿名函数(Anonymous Function)是表示“内联”方法 ...
- 深入学习C#匿名函数、委托、Lambda表达式、表达式树类型——Expression tree types
匿名函数 匿名函数(Anonymous Function)是表示“内联”方法定义的表达式.匿名函数本身及其内部没有值或者类型,但是可以转换为兼容的委托或者表达式树类型(了解详情).匿名函数转换的计算取 ...
- 匿名函数、委托和Lambda表达式
匿名函数 匿名函数(Anonymous Function)是表示“内联”方法定义的表达式.匿名函数本身及其内部没有值或者类型,但是可以转换为兼容的委托或者表达式树类型(了解详情).匿名函数转换的计算取 ...
- [深入学习C#] 匿名函数、委托和Lambda表达式
匿名函数 匿名函数(Anonymous Function)是表示“内联”方法定义的表达式.匿名函数本身及其内部没有值或者类型,但是可以转换为兼容的委托或者表达式树类型(了解详情).匿名函数转换的计算取 ...
- 系统预定义委托与Lambda表达式
NET中那些所谓的新语法之三:系统预定义委托与Lambda表达式 开篇:在上一篇中,我们了解了匿名类.匿名方法与扩展方法等所谓的新语法,这一篇我们继续征程,看看系统预定义委托(Action/Fun ...
- 十二、C# 委托与Lambda表达式(匿名方法的另一种写法)
委托与Lambda表达式 1.委托概述 2.匿名方法 3.语句Lambda 4.表达式Lambda 5.表达式树 一.委托概述 相当于C++当中的方法指针,在C#中使用delegate 委托来 ...
- C# Note2:委托(delegate) & Lambda表达式 & 事件(event)
前言 本文主要讲述委托和Lambda表达式的基础知识,以及如何通过Lambda表达式实现委托调用,并阐述.NET如何将委托用作实现事件的方式. 参考:C#高级编程 1.什么是委托(delegate)? ...
- 使用Java函数接口及lambda表达式隔离和模拟外部依赖更容易滴单测
概述 单测是提升软件质量的有力手段.然而,由于编程语言上的支持不力,以及一些不好的编程习惯,导致编写单测很困难. 最容易理解最容易编写的单测,莫过于独立函数的单测.所谓独立函数,就是只依赖于传入的参数 ...
- 委托学习过程及委托、Lambda表达式和匿名方法的关系总结及事件总结
第一章,当开始学习委托的时候,我们会问什么是委托?为什么要学习委托? 一,什么是委托? 委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法, ...
随机推荐
- ASP.NET MVC — 第 4 天
ASP.NET MVC — 第 4 天 目录 第 1 天 第 2 天 第 3 天 第 4 天 第 5 天 第 6 天 第 7 天 0. 前言 欢迎来到第四天的 MVC 系列学习中.如果你直接开始学习今 ...
- HDU 5052 Yaoge’s maximum profit 光秃秃的树链拆分 2014 ACM/ICPC Asia Regional Shanghai Online
意甲冠军: 特定n小点的树权. 以下n每一行给出了正确的一点点来表达一个销售点每只鸡价格的格 以下n-1行给出了树的侧 以下Q操作 Q行 u, v, val 从u走v,程中能够买一个鸡腿,然后到后面卖 ...
- JQUERY 选择
jQuery 选择器 jQuery 采用 CSS 一个选择选择 HTML 元素. $("p") 选取 <p> 元素. $("p.intro") 选取 ...
- printf 对齐
printf关于对其的问题(参考有关博客加上自己的一些总结) 1.关于左对齐或右对齐问题, 默认的如果%后没有“-”是右对齐的,如果%后跟“0”,不足的个数用0来填充, 例如:printf(&qu ...
- BIEE在creating domain步骤停止的解决的方法
1.错误现象: biee11g creating domain csf entries will not be parsed since the adminserver is unreachable ...
- 读取上传的CSV为DataTable
csv导入文件会把每列的数据用英文逗号分割开来,如果遇到某列中包含英文逗号,则会把该列用英文双引号进行包装. 如果csv文件中某列的数据本身包含英文逗号,应该使用读取字符串的方式进行解析数据,如csv ...
- 纠错《COM技术内幕》之ProgID
近期在看<COM技术内幕>,看到第六章时发现该章节在解释ProgID时有点错误,特此记录一下,也给正在学习COM的小伙伴们一个提示. 并且我发现该问题存在于一些非常多大型软件的COM组件中 ...
- UML之轻松入门(2)-掌握Junit,让我们的开发更高效
使用UML不仅能够形象化的表达我们的程序思想,并且能够帮助我们提高程序的质量.一个杂乱无章的程序让维护者望而生畏,其成本也可想而知.在面向程序设计(OOD)中有5条原则是帮助我们设计一个高效 ...
- 【百度地图API】如何制作可拖拽的沿道路测距
原文:[百度地图API]如何制作可拖拽的沿道路测距 摘要: 地图测距,大家都会,不就map.getDistance麼.可是,这只能测任意两点的直线距离,用途不够实际啊.比如,我想测试北京天安门到北京后 ...
- Socket 由浅入深系列--------- 简单实现编程(三)
socket 由浅入深 原理(一)介绍了一些,以下也就是简单实现,并未考虑其它性能化! 使用TCP的server客户机举例 server 设置一个简单的TCPserver涉及下列步骤: 调用 sock ...