抽象类

抽象类 是一些或所有成员未实现的类,以便派生类可以提供实现。

语法

// 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 属性。

在前面的语法中, 辅助功能修饰符 可以是 publicprivate 也可以 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

另请参阅