F#探险之旅(四):面向对象编程(下)(转)
2011-01-14 13:01
155 查看
类的继承
在前面(面向对象(中))介绍过的对象表达式中,已经涉及到了类的继承,我们可以覆盖类的成员实现,然后创建新类的实例。这一节里,我们来看看常规的继承机制,熟悉C#的开发人员会感到更亲切。
运行结果为:
这里Base类为基类,有一个字段state,Sub继承了它,并有一个新字段otherState,下面的代码可以看到,此时Sub类继承了
Base类的state字段。需要注意的是,这里的Base必须要有一个无参的构造函数,否则不能通过编译。其原因是,跟C#一样,在初始化派生类时会调
用基类的构造函数,而F#中类没有默认的构造函数,所以必须显式添加构造函数。而如果基类的构造函数带有参数的话,派生类的构造函数写法也有所不同。
运行结果:
这个结果说明确实调用了基类的构造函数。Inherit Base(0)则说明,我们必须选择正确的构造函数。
类的方法
定义方法时可以使用4个相关的关键字,即member、override、abstract、default。在C#中,类的方法大体上可分为三种
情况。一是在基类中定义,派生类中不能覆盖的;二是在基类中定义,派生类可以覆盖的(使用virtual关键字);三是抽象方法(或接口方法),派生类需
要提供实现的(除非派生类也是抽象类或接口)。对于情况一,可以简单地使用member关键字;情况二,可同时使用abstract和default(或
override),abstract说明该方法可以进行覆盖,default则提供了默认实现;情况三,可仅仅使用abstract,同时为类添加特性
AbstractClass,说明该方法是抽象类的一个抽象方法,必须在派生类中提供实现。
运行结果为:
可以看到JiggleState方法继承了Base的实现,而WiggleState则覆盖了Base的默认实现。
访问基类的成员
如果你曾写过Page类的派生类,那么很可能写过base.OnLoad()这样的代码。“base”用来引用基类中的成员,在F#中稍有不同。就像没有this一样,base也是没有的,需要给它手工指定名称。
这里定义Form类的派生类RectangleForm,注意在inherit后面的as base,这里的base就是给基类起的名字,这个名字是任意的。在覆盖OnPaint时就调用了基类的OnPaint方法。
属性和索引器
属性(Property)和索引器(Indexer)是.NET中重要的“语法糖”特性,它们使得我们的代码更为直观、简洁,而它们本质上是方法。
在Properties类中定义属性MyProp,这里我们可以看到熟悉的get/set变成了两个方法get/set,参数y就是C#中的value。
索引器是一种特殊的属性(又称含参属性)。在C#中,索引器本质上是名为Item的方法。在F#中,我们也可以使用Item之外的名字。
这里的Indexers类定义了两个索引器:Item和MyString,通过Reflector可以看到,它们的签名相同,都是“public
string this[int i] { get; set;
}”,在C#中这是不允许的。C#中索引器的默认名称为Item,对于这里的Item来说,可以用两种方式访问它,而MyString就只有一种了。如果
需要考虑同其它语言的兼容性,建议使用Item定义属性。
类的静态方法
静态方法的定义是实例方法类似,不过要用到static关键字。访问静态方法时,不需要建立类的实例,可以通过类直接访问,如下面的ReverseString方法。
运行结果为:
定义委托
在C#中,通过委托可以像“值”那样去处理方法。显然,在F#中不需要这样,因为函数本来就是当作值来看待的。但是考虑到与其它语言的交互,有时也需要定义委托。
这里myInst是委托MyDelegate的一个实例,我们通过Invoke来调用它。运行结果为:
定义结构类型
结构(struct)与类(class)的区别就是我们常说的值类型和引用类型的区别,在此不再赘述。下面定义的结构IpAddress用于表示IP地址。
定义枚举类型
枚举类型定义了一组符号名称和数值对,本质上讲,枚举类型就是定义了一组常数字段的结构。F#中枚举的定义也是很简单的。
如果你希望定义位标记枚举,可以使用System.Flags特性。
小结
至此,F#中的面向对象编程范式介绍完毕,我们手中的F#也变得更为锐利。本文首先介绍了类的继承、类的方法(虚方法、抽象方法等)、访问基类等跟
继承相关的概念;接着是类的属性和索引器、类的静态方法这些类的特殊成员;最后讨论了如何定义委托、结构类型、枚举类型等特殊类型。相信有了这些知识,我
们完全可以使用F#代替C#来编写类库了。学习这些知识的过程,也给了我们一个从新的角度了解.NET
Framework的机会。在学习了F#的三种主要编程范式之后,下一步该考虑如何在实战中应用它,比如如何组织规模较大的程序,如何建立UI,如何与其
它.NET语言进行交互等等,在后续的随笔中将逐步介绍这些内容。
注意:本文中的代码均在F# 1.9.4.17版本下编写,在F# CTP 1.9.6.0版本下可能不能通过编译。
在前面(面向对象(中))介绍过的对象表达式中,已经涉及到了类的继承,我们可以覆盖类的成员实现,然后创建新类的实例。这一节里,我们来看看常规的继承机制,熟悉C#的开发人员会感到更亲切。
F# Code - 类的继承 #light type Base = class val state : int new() = { state = 0} end type Sub = class inherit Base val otherState : int new() = { otherState = 1 } end let myObj = new Sub() printfn "myObj.state = %i, myObj.otherState = %i" myObj.state myObj.otherState System.Console.Read()
运行结果为:
Output myObj.state = 0, myObj.otherState = 1
这里Base类为基类,有一个字段state,Sub继承了它,并有一个新字段otherState,下面的代码可以看到,此时Sub类继承了
Base类的state字段。需要注意的是,这里的Base必须要有一个无参的构造函数,否则不能通过编译。其原因是,跟C#一样,在初始化派生类时会调
用基类的构造函数,而F#中类没有默认的构造函数,所以必须显式添加构造函数。而如果基类的构造函数带有参数的话,派生类的构造函数写法也有所不同。
F# Code - 基类具有含参的构造函数 #light type Base = class val state : int new(st) = { state = st} then printfn "Init base class" end type Sub = class inherit Base val otherState : int new() = { inherit Base(0) otherState = 1 } then printfn "Init sub class" end let myObj = new Sub() printfn "myObj.state = %i, myObj.otherState = %i" myObj.state myObj.otherState System.Console.Read()
运行结果:
Output Init base class Init sub class myObj.state = 0, myObj.otherState = 1
这个结果说明确实调用了基类的构造函数。Inherit Base(0)则说明,我们必须选择正确的构造函数。
类的方法
定义方法时可以使用4个相关的关键字,即member、override、abstract、default。在C#中,类的方法大体上可分为三种
情况。一是在基类中定义,派生类中不能覆盖的;二是在基类中定义,派生类可以覆盖的(使用virtual关键字);三是抽象方法(或接口方法),派生类需
要提供实现的(除非派生类也是抽象类或接口)。对于情况一,可以简单地使用member关键字;情况二,可同时使用abstract和default(或
override),abstract说明该方法可以进行覆盖,default则提供了默认实现;情况三,可仅仅使用abstract,同时为类添加特性
AbstractClass,说明该方法是抽象类的一个抽象方法,必须在派生类中提供实现。
F# Code - 类继承时的方法 #light type Base = class val mutable state: int new() = { state = 0 } member x.JiggleState y = x.state <- y abstract WiggleState: int -> unit default x.WiggleState y = x.state <- y + x.state end type Sub = class inherit Base new() = {} override x.WiggleState y = x.state <- y &&& x.state end let myBase = new Base() let mySub = new Sub() let test(c: #Base) = c.JiggleState 1 print_int c.state print_newline() c.WiggleState 3 print_int c.state print_newline() print_endline "base class: " test myBase print_endline "sub class: " test mySub
运行结果为:
Output base class: 1 4 sub class: 1 1
可以看到JiggleState方法继承了Base的实现,而WiggleState则覆盖了Base的默认实现。
访问基类的成员
如果你曾写过Page类的派生类,那么很可能写过base.OnLoad()这样的代码。“base”用来引用基类中的成员,在F#中稍有不同。就像没有this一样,base也是没有的,需要给它手工指定名称。
F# Code - 访问基类的成员 #light open System open System.Drawing open System.Windows.Forms type RectangleForm(color) = class inherit Form() as base override x.OnPaint(e) = e.Graphics.DrawRectangle(color, 10, 10, x.Width - 30, x.Height - 50) base.OnPaint(e) override x.OnResize(e) = x.Invalidate() base.OnResize(e) end let form = new RectangleForm(Pens.Blue) [<STAThread>] do Application.Run(form)
这里定义Form类的派生类RectangleForm,注意在inherit后面的as base,这里的base就是给基类起的名字,这个名字是任意的。在覆盖OnPaint时就调用了基类的OnPaint方法。
属性和索引器
属性(Property)和索引器(Indexer)是.NET中重要的“语法糖”特性,它们使得我们的代码更为直观、简洁,而它们本质上是方法。
F# Code - 定义属性 #light open System type Propreties() = class let mutable rand = new Random() member x.MyProp with get() = rand.Next() and set y = rand <- new Random(y) end let prop = new Propreties() prop.MyProp <- 10 printfn "%d" prop.MyProp printfn "%d" prop.MyProp
在Properties类中定义属性MyProp,这里我们可以看到熟悉的get/set变成了两个方法get/set,参数y就是C#中的value。
索引器是一种特殊的属性(又称含参属性)。在C#中,索引器本质上是名为Item的方法。在F#中,我们也可以使用Item之外的名字。
F# Code - 定义索引器 #light type Indexers(vals : string[]) = class member x.Item with get(i) = vals.[i] and set(i, v) = vals.[i] <- v member x.MyString with get(i) = vals.[i] and set(i, v) = vals.[i] <- v end let indexer = new Indexers [| "One"; "Two"; "Three"; "Four" |] indexer.[0] <- "Five" indexer.Item(2) <- "Six" indexer.MyString(3) <- "Seven" print_endline indexer.[0] print_endline(indexer.Item(1)) print_endline(indexer.MyString(2)) print_endline(indexer.MyString(3))
这里的Indexers类定义了两个索引器:Item和MyString,通过Reflector可以看到,它们的签名相同,都是“public
string this[int i] { get; set;
}”,在C#中这是不允许的。C#中索引器的默认名称为Item,对于这里的Item来说,可以用两种方式访问它,而MyString就只有一种了。如果
需要考虑同其它语言的兼容性,建议使用Item定义属性。
类的静态方法
静态方法的定义是实例方法类似,不过要用到static关键字。访问静态方法时,不需要建立类的实例,可以通过类直接访问,如下面的ReverseString方法。
F# Code - 定义静态方法 #light type MyClass = class static member ReverseString(s: string) = let chars = s.ToCharArray() let reversedChars = Array.rev chars new string(reversedChars) end let myString = MyClass.ReverseString "dlrow olleH" print_string myString
运行结果为:
Output Hello world
定义委托
在C#中,通过委托可以像“值”那样去处理方法。显然,在F#中不需要这样,因为函数本来就是当作值来看待的。但是考虑到与其它语言的交互,有时也需要定义委托。
F# Code - 定义委托 #light type MyDelegate = delegate of int -> unit let myInst = new MyDelegate(fun i -> print_int i) let intList = [1; 2; 3] intList |> List.iter(fun i -> myInst.Invoke(i))
这里myInst是委托MyDelegate的一个实例,我们通过Invoke来调用它。运行结果为:
Output 123
定义结构类型
结构(struct)与类(class)的区别就是我们常说的值类型和引用类型的区别,在此不再赘述。下面定义的结构IpAddress用于表示IP地址。
F# Code - 定义结构类型 #light type IpAddress = struct val first: byte val second: byte val third: byte val fourth: byte new(first, second, third, fourth) = { first = first; second = second; third = third; fourth = fourth } override x.ToString() = Printf.sprintf "%O.%O.%O.%O" x.first x.second x.third x.fourth member x.GetBytes() = x.first, x.second, x.third, x.fourth end
定义枚举类型
枚举类型定义了一组符号名称和数值对,本质上讲,枚举类型就是定义了一组常数字段的结构。F#中枚举的定义也是很简单的。
F# Code - 定义枚举类型 #light type Season = | Spring | Summer | Autumn | Winter
如果你希望定义位标记枚举,可以使用System.Flags特性。
小结
至此,F#中的面向对象编程范式介绍完毕,我们手中的F#也变得更为锐利。本文首先介绍了类的继承、类的方法(虚方法、抽象方法等)、访问基类等跟
继承相关的概念;接着是类的属性和索引器、类的静态方法这些类的特殊成员;最后讨论了如何定义委托、结构类型、枚举类型等特殊类型。相信有了这些知识,我
们完全可以使用F#代替C#来编写类库了。学习这些知识的过程,也给了我们一个从新的角度了解.NET
Framework的机会。在学习了F#的三种主要编程范式之后,下一步该考虑如何在实战中应用它,比如如何组织规模较大的程序,如何建立UI,如何与其
它.NET语言进行交互等等,在后续的随笔中将逐步介绍这些内容。
注意:本文中的代码均在F# 1.9.4.17版本下编写,在F# CTP 1.9.6.0版本下可能不能通过编译。
相关文章推荐
- F#探险之旅(四):面向对象编程(中)(转)
- ART世界探险(8) - 面向对象编程
- 一 面向对象编程前言
- Javascript面向对象编程(二):构造函数的继承
- C++面向对象编程,继承,数据抽象,动态绑定
- JavaScript 面向对象编程
- javascript面向对象编程(一)
- javascript面向对象编程(实践之无刷新分页插件)
- 面向对象编程中的聚合与耦合
- 面向对象编程的最高准则-开闭原则
- 面向对象编程和面向接口编程——加深复习
- python3.2 简单面向对象编程
- 第五章 面向对象编程(一)
- SpringMVC深度探险(一) —— SpringMVC前传
- 面向对象编程中的继承和组合的简单比较
- Python源码 -- C语言实现面向对象编程(基类&派生类&多态)
- 2. 面向对象编程的特征
- [Javascript] 面向对象编程思想
- Javascript 进阶 面向对象编程 继承的一个例子
- PHP 面向对象编程和设计模式 (5/5) - PHP 命名空间的使用及名称解析规则