抽象類別
抽象類別,這種類別不見得會實作出所有或部分成員,而是留待衍生類別各自提供實作。
語法
// 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
關鍵字一起出現在個別行上。
抽象類別的型別定義可以包含完整定義的成員,但是也可以包含抽象成員。 抽象成員的語法在先前的語法中個別顯示。 在此語法中,成員的 型別特徵標記 是一份清單,其中包含按照順序的參數型別和傳回型別,並依適用於局部調用和元組參數的 ->
標記和/或 *
標記分隔。 抽象成員型別特徵標記的語法與特徵標記檔案中使用的語法,以及由 IntelliSense 在 [Visual Studio Code 編輯器] 中顯示的語法相同。
下列程式碼說明抽象類別 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