類型擴充 (F#)
類型擴充可讓您將新成員加入至先前定義的物件類型。
// Intrinsic extension.
type typename with
member self-identifier.member-name =
body
...
[ end ]
// Optional extension.
type typename with
member self-identifier.member-name =
body
...
[ end ]
備註
類型擴充有兩種,其語法和行為稍有不同。 「內建擴充」(Intrinsic Extension) 是出現在與所擴充類型相同的命名空間或模組、相同原始檔,或相同組件 (DLL 或可執行檔) 中的擴充。 「選擇性擴充」(Optional Extension) 是出現在所擴充類型之原始模組、命名空間或組件外部的擴充。 當反映檢查類型時,類型上會出現內建擴充,但不會出現選擇性擴充。 選擇性擴充必須是在模組中,而且只有在包含擴充的模組開啟時才會在範圍內。
在先前的語法中,typename 表示要擴充的類型。 任何可存取的類型都可以擴充,但是類型名稱必須是實際的類型名稱,而不是類型縮寫。 您可以在一個類型擴充中定義多個成員。 self-identifier 表示叫用的物件執行個體,就像一般成員一樣。
end 在輕量型語法中是選擇性關鍵字。
使用類型擴充中定義的成員時,就像使用類別類型上的其他成員一樣。 如同其他成員,這些成員可以是靜態或執行個體成員。 這些方法也稱為「擴充方法」(Extension Method),而屬性則稱為「擴充屬性」(Extension Property),以此類推。 選擇性擴充成員會編譯為靜態成員,其物件執行個體會隱含傳遞做為第一個參數。 不過,根據他們的宣告方式,他們會表現得向執行個體成員或靜態成員。 隱含擴充成員是包含做為類型的成員,因此使用時不受限制。
擴充方法不可以是虛擬或抽象方法。 它們可以多載同名的其他方法,但是編譯器會在進行模稜兩可的呼叫時提供不可擴充方法的偏好設定。
如果一個類型有多個內建類型擴充,則所有成員都必須是唯一。 對於選擇性類型擴充,在相同類型但不同類型擴充中的成員可以有相同名稱。 只有在用戶端程式碼開啟兩個定義了相同成員名稱的不同範圍時,才會發生語意模糊錯誤。
在下列範例中,模組中的類型有內建類型擴充。 對於該模組外的用戶端程式碼,就各方面來說,此類型擴充會顯示為類型的一般成員。
module MyModule1 =
// Define a type.
type MyClass() =
member this.F() = 100
// Define type extension.
type MyClass with
member this.G() = 200
module MyModule2 =
let function1 (obj1: MyModule1.MyClass) =
// Call an ordinary method.
printfn "%d" (obj1.F())
// Call the extension method.
printfn "%d" (obj1.G())
您可以使用內建類型擴充將類型定義分為不同區段。 這在管理大型類型定義時很有用,例如,將編譯器產生的程式碼和撰寫的程式碼分開,或者將不同人員所建立或與不同功能關聯的程式碼群組在一起。
在下列範例中,選擇性類型擴充會以呼叫靜態成員 Parse 的擴充方法 FromString 來擴充 System.Int32 類型。 testFromString 方法會示範呼叫新成員的方式,就如同呼叫任何執行個體成員一樣。
// Define a new member method FromString on the type Int32.
type System.Int32 with
member this.FromString( s : string ) =
System.Int32.Parse(s)
let testFromString str =
let mutable i = 0
// Use the extension method.
i <- i.FromString(str)
printfn "%d" i
testFromString "500"
新執行個體成員看起來就像 IntelliSense 中 Int32 類型的任何其他方法,但是只有在包含擴充的模組已開啟或在範圍內才會如此。
泛型擴充方法
在 F# 3.1 之前, F# 編譯器不支援使用泛型類型變數、陣列類型、Tuple 類型或 F# 函式類型為「this」參數的 C# 樣式擴充方法。 F# 3.1 支援使用這些擴充成員。
例如,在 F# 3.1 程式碼中,您可以使用具有簽章的擴充方法,與 C# 中的下列語法類似:
static member Method<T>(this T input, T other)
泛型類型參數受限制時,這個方法特別有用。 此外,您現在可以在 F# 程式碼中宣告像這樣的擴充成員,以及定義在語意上豐富的其他擴充方法。 在 F# 中,您通常會定義擴充成員,如下列範例所示:
type seq<’T> with
/// Repeat each element of the sequence n times
member xs.RepeatElements(n: int) =
seq { for x in xs do for i in 1 .. n do yield x }
不過,對於泛型類型,類型變數不可限制。 您現在可以用 F# 宣告 C# 樣式擴充成員,解決這項限制。 當您合併這種宣告與 F# 內嵌功能時,您可以將泛型演算法呈現為擴充成員。
請考慮下列宣告:
[<Extension>]
type ExtraCSharpStyleExtensionMethodsInFSharp () =
[<Extension>]
static member inline Sum(xs: seq<’T>) = Seq.sum xs
使用這個宣告,您可以撰寫類似下列範例的程式碼。
let listOfIntegers = [ 1 .. 100 ]
let listOfBigIntegers = [ 1I to 100I ]
let sum1 = listOfIntegers.Sum()
let sum2 = listOfBigIntegers.Sum()
在此程式碼中,藉由定義單一擴充成員,將相同的一般算術程式碼套用至兩種類型的清單,而不使用多載。