共用方式為


型別延伸模組

類型延伸模組(也稱為 擴增)是一系列功能,可讓您將新成員新增至先前定義的物件類型。 這三個功能包括:

  • 內建類型延伸
  • 選擇性類型延伸模組
  • 擴充方法

每個都可以在不同的案例中使用,而且有不同的取捨。

語法

// Intrinsic and optional extensions
type typename with
    member self-identifier.member-name =
        body
    ...

// Extension methods
open System.Runtime.CompilerServices

[<Extension>]
type Extensions() =
    [<Extension>]
    static member extension-name (ty: typename, [args]) =
        body
    ...

內建類型延伸

內建型別延伸是擴充使用者定義型別的類型延伸模組。

內建型別延伸必須定義在相同的檔案 中,以及 與它們所擴充的類型相同的命名空間或模組中。 任何其他定義都會導致它們成為 選擇性類型延伸模組。

內建型別延伸有時是將功能與類型宣告分開的更簡潔方式。 下列範例示範如何定義內建型別延伸模組:

namespace Example

type Variant =
    | Num of int
    | Str of string
  
module Variant =
    let print v =
        match v with
        | Num n -> printf "Num %d" n
        | Str s -> printf "Str %s" s

// Add a member to Variant as an extension
type Variant with
    member x.Print() = Variant.print x

使用類型延伸模組可讓您分隔下列各項:

  • 型別 Variant 的宣告
  • 根據類別的「圖形」列印 Variant 的功能
  • 使用物件樣式 .表示法存取列印功能的方法

這是將所有項目定義為 上的 Variant成員的替代方案。 雖然這不是原本更好的方法,但在某些情況下,它可能是更清楚的功能表示法。

內建型別延伸會編譯為其增強型別的成員,並在反映檢查類型時出現在類型上。

選擇性類型延伸模組

選擇性類型延伸模組是出現在要擴充之類型的原始模組、命名空間或元件外部的延伸模組。

選擇性類型延伸很適合用來擴充您尚未自行定義的類型。 例如:

module Extensions

type IEnumerable<'T> with
    /// Repeat each element of the sequence n times
    member xs.RepeatElements(n: int) =
        seq {
            for x in xs do
                for _ in 1 .. n -> x
        }

您現在可以存取RepeatElements,只要模組在正在使用的範圍中開啟,就可以存取它的成員ExtensionsIEnumerable<T>

當反映檢查時,選擇性的延伸模組不會出現在擴充類型上。 選擇性擴充功能必須位於模組中,而且只有在包含延伸模組的模組已開啟或處於範圍時,才會在範圍內。

選擇性擴充成員會編譯為靜態成員,而該靜態成員會隱含地傳遞對象實例作為第一個參數。 不過,根據實例成員或靜態成員的宣告方式,它們的行為就如同它們一樣。

C# 或 Visual Basic 取用者也看不到選用的擴充成員。 它們只能在其他 F# 程式代碼中使用。

內建和選擇性類型延伸的泛型限制

您可以在類型變數受限的泛型型別上宣告類型延伸模組。 需求是延伸宣告的條件約束符合宣告類型的條件約束。

不過,即使宣告型別與型別延伸之間的條件約束相符,還是可由延伸成員主體推斷的條件約束,而該主體會對型別參數施加與宣告型別不同的需求。 例如:

open System.Collections.Generic

// NOT POSSIBLE AND FAILS TO COMPILE!
//
// The member 'Sum' has a different requirement on 'T than the type IEnumerable<'T>
type IEnumerable<'T> with
    member this.Sum() = Seq.sum this

無法讓此程式碼使用選擇性類型延伸模組:

  • 相同,成員Sum'T 型別擴充所定義的條件約束不同。static member get_Zerostatic member (+)
  • 將型別延伸修改為具有 與 相同的條件約束 Sum ,將不再符合 上 IEnumerable<'T>定義的條件約束。
  • 變更 member this.Summember inline this.Sum 會提供類型條件約束不相符的錯誤。

想要的是靜態方法,這些方法會「在空間中浮動」,而且可以呈現為擴充型別。 這是需要擴充方法的地方。

擴充方法

最後,擴充方法(有時稱為“C# 樣式擴充成員”)可以在 F# 中宣告為類別上的靜態成員方法。

當您想要在限制類型變數的泛型型別上定義擴充功能時,擴充方法會很有用。 例如:

namespace Extensions

open System.Collections.Generic
open System.Runtime.CompilerServices

[<Extension>]
type IEnumerableExtensions =
    [<Extension>]
    static member inline Sum(xs: IEnumerable<'T>) = Seq.sum xs

使用時,此程式代碼會讓它 Sum 看起來像是在 上 IEnumerable<T>定義,只要 Extensions 已開啟或位於範圍內。

若要讓延伸模組可供 VB.NET 程式代碼使用,元件層級需要額外的功能 ExtensionAttribute

module AssemblyInfo
open System.Runtime.CompilerServices
[<assembly:Extension>]
do ()

其他備註

類型延伸也有下列屬性:

  • 任何可以存取的類型都可以擴充。
  • 內建和選擇性類型延伸模組可以定義 任何 成員類型,而不只是方法。 因此,您也可以使用擴充屬性,例如。
  • self-identifier 語法中的標記代表所叫用之型別的實例,就像一般成員一樣。
  • 擴充成員可以是靜態或實例成員。
  • 類型延伸的類型變數必須符合宣告類型的條件約束。

類型延伸模組也有下列限制:

  • 類型延伸模組不支援虛擬或抽象方法。
  • 類型延伸不支援覆寫方法做為擴增。
  • 類型延伸模組不支持 靜態解析的類型參數
  • 選擇性的型別擴充不支援擴充建構函式。
  • 類型延伸無法在 類型縮寫上定義。
  • 類型延伸模組無效 byref<'T> (雖然可以宣告它們)。
  • 類型延伸對屬性無效(雖然可以宣告它們)。
  • 您可以定義擴充功能,以多載相同名稱的其他方法,但如果有模棱兩可的呼叫,F# 編譯程式會提供非擴充方法的喜好設定。

最後,如果一種類型存在多個內部類型延伸,則所有成員都必須是唯一的。 針對選擇性類型延伸模組,相同類型之不同類型延伸模組中的成員可以有相同的名稱。 只有在用戶端程式代碼開啟兩個定義相同成員名稱的不同範圍時,才會發生模棱兩可的錯誤。

另請參閱