方法是與型別相關聯的函式。 在面向物件程序設計中,方法可用來公開和實作物件和型別的功能和行為。
語法
// Instance method definition.
[ attributes ]
member [inline] self-identifier.method-name parameter-list [ : return-type ] =
method-body
// Static method definition.
[ attributes ]
static member [inline] method-name parameter-list [ : return-type ] =
method-body
// Abstract method declaration or virtual dispatch slot.
[ attributes ]
abstract member method-name : type-signature
// Virtual method declaration and default implementation.
[ attributes ]
abstract member method-name : type-signature
[ attributes ]
default self-identifier.method-name parameter-list [ : return-type ] =
method-body
// Override of inherited virtual method.
[ attributes ]
override self-identifier.method-name parameter-list [ : return-type ] =
method-body
// Optional and DefaultParameterValue attributes on input parameters
[ attributes ]
[ modifier ] member [inline] self-identifier.method-name ([<Optional; DefaultParameterValue( default-value )>] input) [ : return-type ]
備註
在上一個語法中,您可以看到各種形式的方法宣告和定義。 在較長的方法主體中,換行符會遵循等號 (=),而整個方法主體會縮排。
屬性可以套用至任何方法宣告。 它們位於方法定義的語法之前,通常會列在個別行上。 如需詳細資訊,請參閱 屬性。
方法可以標示 inline為 。 如需 的相關信息 inline,請參閱 內嵌函式。
非內嵌方法可以在 類型內以遞歸方式使用;不需要明確使用 rec 關鍵詞。
實例方法
實例方法會以 member 關鍵詞和 自我標識符宣告,後面接著句號 (.) 和方法名稱和參數。 如同系 let 結的情況, 參數清單 可以是模式。 一般而言,您會以元組形式括住方法參數,也就是以其他 .NET Framework 語言建立方法時,方法在 F# 中出現的方式。 不過,捲曲形式(以空格分隔的參數)也是常見的,也支援其他模式。
下列範例說明非抽象實例方法的定義和使用方式。
type SomeType(factor0: int) =
let factor = factor0
member this.SomeMethod(a, b, c) = (a + b + c) * factor
member this.SomeOtherMethod(a, b, c) = this.SomeMethod(a, b, c) * factor
在實例方法中,請勿使用自我標識符來存取使用 let 系結所定義的欄位。 存取其他成員和屬性時,請使用自我標識符。
靜態方法
關鍵詞 static 是用來指定方法可以在沒有 實例的情況下呼叫,而且與對象實例沒有關聯。 否則,方法是實例方法。
下一節中的範例會顯示以 關鍵詞宣告的 let 欄位、使用 member 關鍵詞宣告的屬性成員,以及以 關鍵詞宣告的 static 靜態方法。
下列範例說明靜態方法的定義和使用方式。 假設這些方法定義位於 SomeType 上一節的類別中。
static member SomeStaticMethod(a, b, c) =
(a + b + c)
static member SomeOtherStaticMethod(a, b, c) =
SomeType.SomeStaticMethod(a, b, c) * 100
抽象和虛擬方法
關鍵詞 abstract 表示方法具有虛擬分派位置,而且類別中可能沒有定義。
虛擬分派位置是內部維護的函式數據表中的專案,可在運行時間用來查閱面向物件型別中的虛擬函式呼叫。 虛擬分派機制是實作 多型的機制,這是面向物件程序設計的重要功能。 至少有一個沒有定義之抽象方法的類別是 抽象類,這表示無法建立該類別的實例。 如需抽象類的詳細資訊,請參閱 抽象類。
抽象方法宣告不包含方法主體。 相反地,方法的名稱後面接著冒號 (:) 和 方法的類型簽章。 方法的類型簽章與 IntelliSense 在 Visual Studio Code 編輯器中的方法名稱上暫停滑鼠指標時所顯示的相同,但不含參數名稱除外。 當您以互動方式運作時,解釋器 fsi.exe也會顯示類型簽章。 方法的類型簽章是藉由列出參數的類型,後面接著傳回型別,並具有適當的分隔符符號來形成。 Curried 參數會以 -> 分隔,而 Tuple 參數則以 *分隔。 傳回值一律會以符號分隔自 -> 變數。 括弧可用來將複雜參數分組,例如當函式類型為參數時,或指出 Tuple 何時視為單一參數,而不是兩個參數。
您也可以將定義新增至 類別並使用 default 關鍵詞來提供抽象方法預設定義,如本主題的語法區塊所示。 在相同類別中具有定義的抽象方法,相當於其他 .NET Framework 語言中的虛擬方法。 不論定義是否存在, abstract 關鍵詞會在 類別的虛擬函式數據表中建立新的分派位置。
不論基類是否實作其抽象方法,衍生類別都可以提供抽象方法的實作。 若要在衍生類別中實作抽象方法,請定義在衍生類別中具有相同名稱和簽章的方法,但使用 override 或 default 關鍵詞,並提供方法主體。 關鍵詞 override 和 default 表示完全相同的東西。
override如果新的方法覆寫基類實作,請使用 ;當您在與原始抽象宣告相同的類別中建立實作時,請使用 default 。 請勿 abstract 在實作基類中宣告為抽象的方法的方法上使用 關鍵詞。
下列範例說明具有預設實作的抽象方法 Rotate ,相當於 .NET Framework 虛擬方法。
type Ellipse(a0: float, b0: float, theta0: float) =
let mutable axis1 = a0
let mutable axis2 = b0
let mutable rotAngle = theta0
abstract member Rotate: float -> unit
default this.Rotate(delta: float) = rotAngle <- rotAngle + delta
下列範例說明覆寫基類方法的衍生類別。 在此情況下,覆寫會變更行為,讓方法不會執行任何動作。
type Circle(radius: float) =
inherit Ellipse(radius, radius, 0.0)
// Circles are invariant to rotation, so do nothing.
override this.Rotate(_) = ()
多載的方法
多載方法是具有指定類型中相同名稱但具有不同自變數的方法。 在 F# 中,通常會使用選擇性自變數,而不是多載的方法。 不過,語言允許多載方法,前提是自變數是 Tuple 形式,而不是 curried 格式。 下列範例示範它:
type MyType(dataIn: int) =
let data = dataIn
member this.DoSomething(a: int) = a + data
member this.DoSomething(a: string) = sprintf "Hello world, %s!" a
let m = MyType(10)
printfn "With int: %d" (m.DoSomething(2)) // With int: 12
printfn "With string: %s" (m.DoSomething("Bill")) // With string: Hello world, Bill!
選擇性引數
F# 支援方法的選擇性引數。 如需 F# 中可用不同形式的選擇性引數的詳細資訊,請參閱 選擇性參數。
範例:屬性和方法
下列範例包含類型,其中包含字段、私用函式、屬性和靜態方法的範例。
type RectangleXY(x1: float, y1: float, x2: float, y2: float) =
// Field definitions.
let height = y2 - y1
let width = x2 - x1
let area = height * width
// Private functions.
static let maxFloat (x: float) (y: float) = if x >= y then x else y
static let minFloat (x: float) (y: float) = if x <= y then x else y
// Properties.
// Here, "this" is used as the self identifier,
// but it can be any identifier.
member this.X1 = x1
member this.Y1 = y1
member this.X2 = x2
member this.Y2 = y2
// A static method.
static member intersection(rect1: RectangleXY, rect2: RectangleXY) =
let x1 = maxFloat rect1.X1 rect2.X1
let y1 = maxFloat rect1.Y1 rect2.Y1
let x2 = minFloat rect1.X2 rect2.X2
let y2 = minFloat rect1.Y2 rect2.Y2
let result: RectangleXY option =
if (x2 > x1 && y2 > y1) then
Some(RectangleXY(x1, y1, x2, y2))
else
None
result
// Test code.
let testIntersection =
let r1 = RectangleXY(10.0, 10.0, 20.0, 20.0)
let r2 = RectangleXY(15.0, 15.0, 25.0, 25.0)
let r3: RectangleXY option = RectangleXY.intersection (r1, r2)
match r3 with
| Some(r3) -> printfn "Intersection rectangle: %f %f %f %f" r3.X1 r3.Y1 r3.X2 r3.Y2
| None -> printfn "No intersection found."
testIntersection