一、继承基类
在F#中,继承一个基类,需要用到的关键字是inherit。在隐式定义类与显式定义类的继承上,两者之间略有差异。在使用隐式定义类的方式中实现继承时,只需要简单的在申明继承的后面立即加上基类的主要构造函数,并且确保提供基类的主构造函数需要的参数,比如:
type BaseClass =
val m_field1 : int
new(x) = { m_field1 = x }
member this.Field1 = this.m_field1
// 隐式构造函数定义类
type ImplicitDerived(field1, field2) =
inherit BaseClass(field1)
let m_field2 : int = field2
member this.Field2 = m_field2
而当使用显式构造定义类实现继承时,将在实现类的构造函数中通过inherit类型来调用基类的构造函数,如:
type ExplicitDerived =
inherit BaseClass
val m_field2 : int
new(field1, field2) =
{
inherit BaseClass(field1)
m_field2 = field2
}
member this.Field2 = this.m_field2
二、方法重载
不像C#,如果一个方法允许子类来重载,可以把它定义成virtual类型,然后在子类中通过override来实现重载,在F#中,为了表达一个属性或者方法可以被重载,则必须把它标记为abstract类型,并且利用default关键字来提供方法或者属性的默认实现,否则编译不通过。在子类中,重载基类的方法还是通过override来实现,下面的代码列举了几个情况的重载,以及定义重载方法时怎么定义参数类型以及返回值:
// 基类
type DataHelprt() =
// 属性
abstract DBType : string
default this.DBType = "MySQL"
// 无参数的无返回值方法
abstract Open : unit -> unit
default this.Open() = "Connection MySQL" |> ignore
// 带参数的无返回值方法
abstract Execute : string -> unit
default this.Execute(sql) = printfn "Execute %s" sql
// 有返回值的方法
abstract GetList : unit -> List<int>
default this.GetList() = []
// 继承
type OracleDataHelper() =
inherit DataHelprt()
override this.DBType = "Oracle"
override this.Open() = "Connection Oracle" |> ignore
override this.GetList() = [1;2;3;4]
三、抽象类以及密封类
定义一个抽象类,只需要在类的定义中应用[<AbstractClass>]属性,否则,你将会得到一个抽象的没有默认实现的错误,当然,也可以提供一个默认实现。但是如果没有应用AbstractClass属性,而又提供了默认实现,则这个就不是抽象类了。我们可以把上面的示例加以修改一下,变成一个抽象类:
[<AbstractClass>]
type DataHelprt() =
// 属性
abstract DBType : string
// 无参数的无返回值方法
abstract Open : unit -> unit
// 带参数的无返回值方法
abstract Execute : string -> unit
// 有返回值的方法
abstract GetList : unit -> List<int>
// 实现抽象类
type OracleDataHelper() =
inherit DataHelprt()
override this.DBType = "Oracle"
override this.Open() = "Connection Oracle" |> ignore
override this.GetList() = [1;2;3;4]
override this.Execute(sql) = ()
如果不想自己写的类别别人继承,可以把类定义为密封类。为了把类定义成密封类,在类的定义处应用[<Sealed>]属性,比如:
[<Sealed>]
type OracleDataHelper() =
inherit DataHelprt()
override this.DBType = "Oracle"
override this.Open() = "Connection Oracle" |> ignore
override this.GetList() = [1;2;3;4]
override this.Execute(sql) = ()
四、类型转换
多态性的一个关键优势就是可以把一个派生类的实例用作基类来看待,所以上面的DataHelprt类,所有继承它的类都已经拥有了DBType 属性和Open等方法。要把类的实例转换成另外的一个类,熟悉C#的开发员应该都很清楚,可以利用as运算符来转换,但是在F#中,类型的转换有两种方式:静态转换、动态转换。
静态转换就是类型向更高层类型转换,最顶层的类就是超类Object。向上转换操作,用到的运算符是:>。比如下面的例子:
[<AbstractClass>]
type Animal() =
abstract member Legs : int
[<AbstractClass>]
type Dog() =
inherit Animal()
abstract member Description : string
override this.Legs = 4
type Pomeranian() =
inherit Dog()
override this.Description = "Furry"
类型转换:
let steve = new Pomeranian()
// 向上转换为其基类
let steveAsDog = steve :> Dog
// 向上转换为其基类
let steveAsAnimal = steve :> Animal
// 向上转换为object
let steveAsObject = steve :> obj
动态转换就是把基类转换成它的具体的子类或者沿着基类的继承树向下转换。因此,这种类型的转换不能被编译器静态检查,动态转换的运算符是:?>,我们可以把上面的例子变更动态转换类型:
let steve = new Pomeranian()
let steveAsObj = steve :> obj
let steveAsDog = steveAsObj :?> Dog
steveAsDog.Description
如果动态转换失败,将在运行时抛出System.InvalidCastException异常