继承用于在面向对象的编程中对“is-a”关系或子级关系进行建模。
指定继承关系
使用类声明中的 inherit
关键字指定继承关系。 以下示例显示了基本语法形式。
type MyDerived(...) =
inherit MyBase(...)
一个类最多可以有一个直接基类。 如果不使用 inherit
关键字指定基类,则类隐式继承自 System.Object
。
继承的成员
如果类继承自另一个类,则派生类的用户可以使用基类的方法和成员,就好像它们是派生类的直接成员一样。
任何允许绑定和构造函数参数都专用于类,因此,无法从派生类访问。
base
关键字在派生类中可用,引用基类实例。 它与自标识符一样使用。
虚拟方法和替代
与其他 .NET 语言相比,虚拟方法(和属性)在 F# 中的工作方式略有不同。 若要声明新的虚拟成员,请使用 abstract
关键字。 无论是否为该方法提供默认实现,都执行此作。 因此,基类中虚拟方法的完整定义遵循以下模式:
abstract member [method-name] : [type]
default [self-identifier].[method-name] [argument-list] = [method-body]
在派生类中,此虚拟方法的重写遵循以下模式:
override [self-identifier].[method-name] [argument-list] = [method-body]
如果省略基类中的默认实现,则基类将成为抽象类。
下面的代码示例演示基类中新虚拟方法 function1
的声明以及如何在派生类中重写它。
type MyClassBase1() =
let mutable z = 0
abstract member function1: int -> int
default u.function1(a: int) =
z <- z + a
z
type MyClassDerived1() =
inherit MyClassBase1()
override u.function1(a: int) = a + 1
构造函数和继承
必须在派生类中调用基类的构造函数。 基类构造函数的参数出现在 inherit
子句的参数列表中。 所使用的值必须从提供给派生类构造函数的参数中确定。
以下代码显示了基类和派生类,其中派生类调用继承子句中的基类构造函数:
type MyClassBase2(x: int) =
let mutable z = x * x
do
for i in 1..z do
printf "%d " i
type MyClassDerived2(y: int) =
inherit MyClassBase2(y * 2)
do
for i in 1..y do
printf "%d " i
对于多个构造函数,可以使用以下代码。 派生类构造函数的第一行是 inherit
子句,字段显示为使用关键字声明的 val
显式字段。 有关详细信息,请参阅 “显式字段: val
关键字”。
type BaseClass =
val string1 : string
new (str) = { string1 = str }
new () = { string1 = "" }
type DerivedClass =
inherit BaseClass
val string2 : string
new (str1, str2) = { inherit BaseClass(str1); string2 = str2 }
new (str2) = { inherit BaseClass(); string2 = str2 }
let obj1 = DerivedClass("A", "B")
let obj2 = DerivedClass("A")
继承的替代方法
如果需要对类型进行轻微修改,请考虑使用对象表达式作为继承的替代方法。 以下示例演示如何使用对象表达式作为创建新派生类型的替代方法:
open System
let object1 =
{ new Object() with
override this.ToString() = "This overrides object.ToString()" }
printfn "%s" (object1.ToString())
有关对象表达式的详细信息,请参阅 对象表达式。
创建对象层次结构时,请考虑使用歧视的联合而不是继承。 歧视联合还可以为共享通用整体类型的不同对象的不同行为建模。 单个区分联合通常不需要多个派生类,这些类是彼此的次要变体。 有关歧视工会的信息,请参阅 歧视联盟。