相信很多人都听说过函数式编程,提到函数式程序设计,脑海里涌现出来更多的是Lisp、Haskell等语言,而C#,似乎我们并不把它当成函数式语言,其实,函数式程序设计并不是只针对某个特定的程序设计语言,而C#,也正一步步使用函数式丰富自己的语言结构,帮助人们更好的实现期望的结果。
函数式程序设计
函数式程序设计把重点放在函数的应用上,函数式程序设计人员以函数为基本模块来建立新函数,这并不是说没有其他语言的成分,而是说函数是程序体系创建的主要构造。
引用透明(Referential transparency)是函数式程序设计领域中的一个重要思想。一个引用透明的函数的返回值只取决于传递给它的参数的值。这正好与指令程序设计的基本思想相反。在指令程序设计中,程序的状态通常会影响函数的返回值。引用透明的函数的数学意义仅存在于函数式程序设计中,这样的函数称为纯函数,没有副作用。
函数式程序设计属于一种定向思维。如果我们愿意按某种方式去思考,则它可以给我们提供有趣的解决方案或者至少思考的源头,它们都与当前程序设计的许多实际问题有关。
C#无法做到像Lisp、Haskell或同属于.NET平台的F#那样很容易实现函数式程序设计,这点我们必须承认,但从各方面来讲,用C#实现函数式程序设计确实是有意义的。
由于C#的函数只能出现在类中,因此它们通常被称为方法。方法可以接受若干个参数,并且可以有一个返回值。
与许多面向对象语言一样,C#类中的方法可以是实例方法,也可以是类方法。而在纯函数式程序设计中,没有类,也没有类的实例——当然,有很多方法保存数据,但通常不是用类来保存数据,它们总是在许多方面表现出不同。
在面向对象环境中,所有其他元素只能出现在类和对象的内部(对象是类实例的另一个说法);而在函数式程序设计中,所有其他元素都出现在函数内部。有些数据保存在函数的局部变量中,就像C#那样定义在方法内部的变量,但这并不是保存数据最理想的方法。
F#把类级别的成员当成全局成员,同时由于得到特殊语法的支持,程序员不需要考虑实际发生的“转换”过程,遗憾的是,在C#中无法实现这一点,但是解决方法是一样的。
为了调用全局级的函数(或者任何其他作用域的函数),必须在类内创建类级别的成员。这些成员要用static关键字。由于它们都封装在类中,因此类中的成员有不同的可见度。大多数函数式设计环境都有不同的封装级别——如模块级或命名空间级——因此除了C#中一些比较复杂的语法外,实际上两者没有多大的区别。
有些函数式语言使用顶级函数或者允许导入模块或命名空间,这样就不需要函数调用的修饰符:
在C#中,这样的调用总是需要一个修饰符,即类名,除非这个函数出现在同一个类的内部:
在计算机程序设计中,重用是一个非常重要的综合问题。函数并不是可重用性的唯一方法,特别在面向对象程序设计中,很快出现了其他方法。作为C#的一个内置功能,它只支持函数的重载作为函数级模块化的直接办法,C#4.0支持命名参数和可选参数,因此重载函数的解析过程变得相当复杂,特别当它与其他相关方法(如在方法调用时进行泛型类型推断)一起使用时。
下面举一个重载方法的简单例子: