類型延伸模組(也稱為 擴增)是一系列功能,可讓您將新成員新增至先前定義的物件類型。 這三個功能包括:
- 內建類型延伸
- 選擇性類型延伸模組
- 擴充方法
每個都可以在不同的案例中使用,而且有不同的取捨。
語法
// 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.Sum為member 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# 編譯程式會提供非擴充方法的喜好設定。
最後,如果一種類型存在多個內部類型延伸,則所有成員都必須是唯一的。 針對選擇性類型延伸模組,相同類型之不同類型延伸模組中的成員可以有相同的名稱。 只有在用戶端程式代碼開啟兩個定義相同成員名稱的不同範圍時,才會發生模棱兩可的錯誤。