本文翻译自F# Overview (III.) - Object Oriented and Imperative Programming
在这一部分我们将了解到一些众所周知的语言特性,现在的大部分语言提供了这种特性.F#尽量使之与上一部分介绍的函数式编程自然的结合在一起.
命令式编程和可变变量
和ML及OCaml语言类似,F#采取及早求值(Eager evaluation)机制,即代码的书写顺序和执行顺序是一致的并且作为参数的表达式是在函数被调用前计算的(和C#,Java,Python一样).这使得在函数式语言里支持命令式编程是语义合理的.前面提到过,F#里的变量默认是不可变的,可以用mutable 关键词使之可变.另外F#支持一些命令式语言的关键词( for ,while),它们是类型为unit的表达式:
及早求值和可变变量的使用使得F#很容易和其它.Net语言(它们基于可变变量)交互.也可以用mutable 创建一个有可变字段的record 类型.
数组
另外一种值可变的类型是.Net里的数组.数组可以用[| .. |] 或者用Array.create创建.可变变量的值用<-操作符修改:
.NET 互操作 .
Net的基础类库是用面向对象的方式构建的,它们几乎全部是可变的.及早求值和对副作用(side-effects)的支持是与.Net互操作时用到的重要特性.下面演示了ResizeArray<T> 的使用 (ResizeArray 是System.Collections.Generic.List的别名,为了避免和 F# 的list 类型混淆):
我们创建泛型实例时用了下划线,因为F#的类型推断算法可以从代码确定它的类型参数是string.然后用add方法修改它的值.最后用Seq.to_list 把它转化成F#里的list. 作为完全兼容.Net的语言,F#提供了创建类(F#里称为object types)的方法,这些类被编译成clr类或接口,所以其它语言可以使用这些类;并且也可以用来扩展其它语言的类.这是个重要的功能使得F#可以访问复杂的类库例如Windows Forms 或者ASP.NET from.
面向对象编程
对象类型(Object Types)
F#的面向对象和NET CLR对OO的支持是类似的.F#支持单类继承,多接口继承,子类,动态类型测试(可以测试一个对象是否是由子类转换来的).最后所以的对象类型继承于一个类型obj,它是 System.Object.的别名.
F#的对象类型可以有字段,构造函数,函数,属性,下面演示了F#里的相关的语法:
MyCell 类型有一个可变字段data,一个属性Data,一个实例方法Print,一个静态方法FromInt ,一个重写的方法ToString. 最后, 它有一个隐式构造函数,它允许我们把构造函数代码直接写到类型定义里.在这个例子里,它给data 字段赋值并且打印一行文字. F#也支持显式构造函数,但很少用到.
上例里我们创建了一个具体对象类型(concrete object type ),它可以被创建实例.接下来我们声明一个接口(F#里称为抽象对象类型abstract object type ) :
F#对面向对象支持的一个有趣地方是,不需要显式指明一个对象类型是抽象的(接口),具体(类)的或部分实现(抽象类)的, F#编译器可以做这件事.抽象对象类型可以被具体类型或者对象表达式(object expression)实现,稍后讨论这个. 继承接口时用interface .. with. 请注意,轻量级F#的语法缩进敏感的,这意味着实现该接口的类型的成员需要有进一步缩进:
F#支持的类型转换有upcast,用来转换一个对象到基类(写作 o :> TargetType);downcast,用来从基类转换回来(写作 o :?> TargetType), 动态类型测试(写作 o :? TargetType), 用来测试是否可以转换到一个类型.
对象表达式(Object expressions)
前面说过,抽象类型也可以被对象表达式实现,这允许不用创建一个具体类而实现接口,当需要从函数返回一个接口时这很好用.如下:
这个例子用了F#里另外一种可变的值引用单元格(reference cell ),它和可变变量很相似,但更灵活(本例中F#编译器不允许使用普通的可变变量). 可变单元格用ref 和一个默认值创建.,用!操作符取值,用:= 修改值. 用new ... with 来实现接口 (对象表达式不可以添加新成员). 本例的引用单元格声明在函数里,然后被一个闭包捕获, 这意味着它将一直存在直到返回对象被回收.
向F#类型里添加成员
可能使用对象的最大好处就是多态,可以在不改变现有代码的情况下修改实现.另一方面F#里的类型如可区分联合或记录暴露了实现细节,有时会造成一些问题,尤其是开发大型程序时. 点提示可以很容易发现类型支持的操作.为了弥补这个问题F#允许为这些类型添加成员:
我们声明了一个类型Variant ,它包含一个数字或者字符串成员,还有一个可以使用点提示的成员Print .除了添加成员(函数或者属性),还可以用之前提过的interface ... with 实现一个接口. 相比把所有代码写在member 里面,还有一种更优雅的写法:
内部扩展(type ... With)往Variant 类里添加了一个成员Print .内部扩展必须和要扩展的类在同一个编译单元(通常在同一个文件). 也可以扩展另外程序集的类型(称为可选扩展,类似于C#里的扩展方法),主要的区别是这些扩展成员只是语法糖,而不是类的一部分. 声明可选扩展使用同样的语法,但是type需要用全名:
我们往.Net的泛型List类里添加了一个方法ToList.请注意要扩展的类不能用F#里的别名,还有可选扩展是由F#编译器做了些事情,所以不大可能在C#里调用. 一般扩展不是很常用,但是一些F#类库(如异步和并行编程)用到了它.
本站技术原创栏目文章均为中睿原创或编译,转载请注明:文章来自中睿,本站保留追究责任的权利。