抽象類別 (F#)
「抽象類別」(Abstract Class) 是使部分或全部成員保持未實作狀態的類別,因此衍生類別可以提供實作。
// 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 屬性。它們可以有已實作和未實作的成員。「抽象」(Abstract) 一詞的用法在套用至類別時與其他 .NET 語言相同;不過,在 F# 中,「抽象」(Abstract) 一詞的用法在套用至方法 (和屬性) 時與其他 .NET 語言稍有不同。在 F# 中,當方法以 abstract 關鍵字標記時,這表示成員在該型別的內部虛擬函式資料表中有項目,稱為「虛擬分派位置」(Virtual Dispatch Slot)。換句話說,方法為虛擬,儘管 F# 語言中不使用 virtual 關鍵字。無論是否實作方法,虛擬方法上都會使用 abstract 關鍵字。虛擬分派位置的宣告與該分派位置的方法定義是分開的。因此,另一個 .NET 語言的虛擬方法宣告和定義,其 F# 對等用法為抽象方法宣告和分開定義的組合,並且具有 default 關鍵字或 override 關鍵字。如需詳細資訊與範例,請參閱方法 (F#)。
只在有已宣告但未定義的抽象方法時,類別才會視為抽象。因此,有抽象方法的類別不一定是抽象類別。除非類別有未定義的抽象方法,否則請勿使用 AbstractClass 屬性。
在上述語法中,accessibility-modifier 可以是 public、private 或 internal。如需詳細資訊,請參閱存取控制 (F#)。
如同其他型別,抽象類別可以有基底類別和一個或多個基底介面。每個基底類別或介面與 inherit 關鍵字出現在單獨一行。
抽象類別的型別定義可以包含完整定義成員,也可以包含抽象成員。在上述語法中,個別顯示抽象成員的語法。在這個語法中,成員的 type signature 是包含依序排列參數型別和傳回型別的清單,型別之間根據局部調用和 Tuple 參數以 -> 語彙基元和 (或) * 語彙基元分隔。抽象成員型別簽章的語法與簽章檔案中所使用的語法以及 Visual Studio 程式碼編輯器中 IntelliSense 所顯示的語法相同。
範例
下列程式碼說明有兩個非抽象衍生類別 Square 和 Circle 的抽象類別 Shape。此範例示範如何使用抽象類別、方法和屬性。在範例中,抽象類別 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
// overriden.
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