分享方式:


方法

方法是與型別相關聯的函式。 在物件導向程式設計中,方法可用來公開和實作物件和型別的功能和行為。

語法

// 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 也會顯示型別特徵標記。 方法的型別特徵標記是由列出參數的類型,後面接著傳回型別以及適當的分隔符號來形成。 局部調用參數會以 -> 分隔,而元組參數則以 * 分隔。 傳回值一律會以 -> 符號與引數分隔。 括號可用來將複雜參數分組,例如當函式型別為參數時,或指出何時將元組視為單一參數,而不是兩個參數。

您也可以藉由將定義新增至類別並使用 default 關鍵字,如本主題的語法區塊所示,提供抽象方法預設定義。 在相同類別中具有定義的抽象方法,相當於其他 .NET Framework 語言中的虛擬方法。 不論定義是否存在,abstract 關鍵字會在類別的虛擬函式資料表中建立新的分派位置。

不論基底類別是否實作其抽象方法,衍生類別都可以提供抽象方法的實作。 若要在衍生類別中實作抽象方法,請定義在衍生類別中具有相同名稱和特徵標記的方法,但是請使用 overridedefault 關鍵字,並提供方法主體。 關鍵字 overridedefault 表示完全相同的意義。 如果新方法覆寫基底類別實作,請使用 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# 中,通常會使用選擇性引數,而不是多載方法。 不過,在語言中允許多載方法,前提是引數採用元組形式,而不是局部調用形式。

選擇性引數

從 F# 4.1 開始,您也可以在方法中具有含預設參數值的選擇性引數。 這是為了協助與 C# 程式碼互通。 下列範例示範語法:

open System.Runtime.InteropServices
// A class with a method M, which takes in an optional integer argument.
type C() =
    member _.M([<Optional; DefaultParameterValue(12)>] i) = i + 1

請注意,針對 DefaultParameterValue 傳入的值必須符合輸入型別。 在上述範例中,是 int。 嘗試將非整數值傳遞至 DefaultParameterValue 會導致編譯錯誤。

範例:屬性和方法

下列範例包含型別,其中包含欄位、私人函式、屬性和靜態方法的範例。

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

另請參閱