抽象类
抽象类是使部分成员或全部成员未实现的类,以便可以由派生类来提供实现。
语法
// Abstract class syntax.
[<AbstractClass>]
type [ accessibility-modifier ] abstract-class-name =
[ inherit base-class-or-interface-name ]
[ abstract-member-declarations-and-member-definitions ]
// Abstract member syntax.
abstract member member-name : type-signature
备注
在面向对象的编程中,抽象类用作层次结构的基类,并表示一组不同的对象类型的常用功能。 如名称“抽象”所示,抽象类通常不直接与问题域中的具体实体对应。 不过,它们确实代表许多不同的具体实体的共同点。
抽象类必须具有 AbstractClass
属性。 它们可以拥有已实现的和未实现的成员。 当应用于类时,“抽象”一词的用法与其他 .NET 语言相同;但当应用于方法(和属性)时,“抽象”一词在 F# 中的用法与它在其他 .NET 语言中的用法稍有不同。 在 F# 中,使用 abstract
关键字标记方法时,这表示成员在该类型的虚函数的内部表中有一个称为“虚拟调度槽”的项。 换句话说,方法为虚方法,但 F# 中未使用 virtual
关键字。 关键字 abstract
用于虚方法,无论该方法是否已实现。 虚拟调度槽的声明与该调度槽的方法定义是独立的。 因此,与其他 .NET 语言中的虚方法声明和定义等效的 F# 等效项是使用 default
关键字或 override
关键字的抽象方法声明和单独定义的组合。 有关详细信息和示例,请参阅方法。
仅当存在已声明但未定义的抽象方法时,才将类视为抽象类。 因此,具有抽象方法的类不一定是抽象类。 请勿使用 AbstractClass 属性,除非类具有未定义的抽象方法。
在前面的语法中,可访问性修饰符可以是 public
、private
或 internal
。 有关详细信息,请参阅访问控制。
与其他类型一样,抽象类可以具有一个基类以及一个或多个基接口。 每个基类或基接口都显示在单独的行中,并带有 inherit
关键字。
抽象类的类型定义可包含完全定义的成员,但也可以包含抽象成员。 在前面的语法中,抽象成员的语法是单独显示的。 在此语法中,成员的类型签名是一个列表,其中包含按顺序排列的参数类型和返回类型(由适用于扩充参数和元组参数的 ->
标记和/或 *
标记分隔)。 抽象成员类型签名的语法与签名文件中使用的语法以及 Visual Studio Code 编辑器中由 IntelliSense 显示的语法相同。
下面的代码阐释一个抽象类 Shape,它具有两个非抽象的派生类(即 Square 和 Circle)。 该示例演示如何使用抽象类、方法和属性。 在此示例中,抽象类 Shape 表示两个具体实体(圆和方形)的通用元素。 所有形状的通用特征(在二维坐标系中)均被提取到 Shape 类中:在网格中的位置、旋转角度以及区域和外围属性。 可以重写这些特征(位置除外),不能更改各个形状的行为。
可以重写旋转方法,就像在 Circle 类中一样,因为它是对称的,所以具有旋转不变性。 因此在 Circle 类中,旋转方法替换为不执行任何操作的方法。
// An abstract class that has some methods and properties defined
// and some left abstract.
[<AbstractClass>]
type Shape2D(x0: float, y0: float) =
let mutable x, y = x0, y0
let mutable rotAngle = 0.0
// These properties are not declared abstract. They
// cannot be overriden.
member this.CenterX
with get () = x
and set xval = x <- xval
member this.CenterY
with get () = y
and set yval = y <- yval
// These properties are abstract, and no default implementation
// is provided. Non-abstract derived classes must implement these.
abstract Area: float with get
abstract Perimeter: float with get
abstract Name: string with get
// This method is not declared abstract. It cannot be
// overridden.
member this.Move dx dy =
x <- x + dx
y <- y + dy
// An abstract method that is given a default implementation
// is equivalent to a virtual method in other .NET languages.
// Rotate changes the internal angle of rotation of the square.
// Angle is assumed to be in degrees.
abstract member Rotate: float -> unit
default this.Rotate(angle) = rotAngle <- rotAngle + angle
type Square(x, y, sideLengthIn) =
inherit Shape2D(x, y)
member this.SideLength = sideLengthIn
override this.Area = this.SideLength * this.SideLength
override this.Perimeter = this.SideLength * 4.
override this.Name = "Square"
type Circle(x, y, radius) =
inherit Shape2D(x, y)
let PI = 3.141592654
member this.Radius = radius
override this.Area = PI * this.Radius * this.Radius
override this.Perimeter = 2. * PI * this.Radius
// Rotating a circle does nothing, so use the wildcard
// character to discard the unused argument and
// evaluate to unit.
override this.Rotate(_) = ()
override this.Name = "Circle"
let square1 = new Square(0.0, 0.0, 10.0)
let circle1 = new Circle(0.0, 0.0, 5.0)
circle1.CenterX <- 1.0
circle1.CenterY <- -2.0
square1.Move -1.0 2.0
square1.Rotate 45.0
circle1.Rotate 45.0
printfn "Perimeter of square with side length %f is %f, %f" (square1.SideLength) (square1.Area) (square1.Perimeter)
printfn "Circumference of circle with radius %f is %f, %f" (circle1.Radius) (circle1.Area) (circle1.Perimeter)
let shapeList: list<Shape2D> = [ (square1 :> Shape2D); (circle1 :> Shape2D) ]
List.iter (fun (elem: Shape2D) -> printfn "Area of %s: %f" (elem.Name) (elem.Area)) shapeList
输出:
Perimeter of square with side length 10.000000 is 40.000000
Circumference of circle with radius 5.000000 is 31.415927
Area of Square: 100.000000
Area of Circle: 78.539816