抽象类 是一些或所有成员未实现的类,以便派生类可以提供实现。
语法
// 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 语言相同;但是,应用于方法(和属性)时,术语 抽象 的使用与其他 .NET 语言的用法略有不同。 在 F# 中,使用关键字标记 abstract 方法时,这表示成员在该类型的虚拟函数的内部表中有一个条目(称为 虚拟调度槽)。 换句话说,该方法是虚拟的,尽管 virtual 该关键字未在 F# 中使用。 无论是否实现该方法,该关键字 abstract 都用于虚拟方法。 虚拟调度槽的声明与该调度槽的方法的定义分开。 因此,另一种 .NET 语言中虚拟方法声明和定义的 F# 等效项是抽象方法声明和单独的定义 default 与关键字或 override 关键字的组合。 有关详细信息和示例,请参阅 方法。
仅当声明但未定义抽象方法时,类才被视为抽象。 因此,具有抽象方法的类不一定是抽象类。 除非类具有未定义的抽象方法,否则不要使用 AbstractClass 属性。
在前面的语法中, 辅助功能修饰符 可以是 public, private 也可以 internal。 有关详细信息,请参阅 访问控制。
与其他类型一样,抽象类可以具有基类和一个或多个基接口。 每个基类或接口都与 inherit 关键字一起显示在单独的行上。
抽象类的类型定义可以包含完全定义的成员,但它也可以包含抽象成员。 抽象成员的语法在前面的语法中单独显示。 在此语法中,成员 的类型签名 是一个列表,其中包含参数类型的顺序和返回类型,根据 curried 和 tupled 参数,用 -> 标记和/或 * 标记分隔。 抽象成员类型签名的语法与在签名文件中使用的语法相同,在 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