共用方式為


泛型

F# 函式值、方法、屬性和匯總類型,例如類別、記錄和差異等位可以是 泛型。 泛型建構至少包含一個類型參數,通常由泛型建構的使用者提供。 泛型函式和類型可讓您撰寫適用於各種類型的程式代碼,而不重複每個類型的程序代碼。 讓程式代碼泛型在 F# 中變得簡單,因為您的程式代碼通常由編譯程式的型別推斷和自動一般化機制隱含推斷為泛型。

語法

// Explicitly generic function.
let function-name<type-parameters> parameter-list =
function-body

// Explicitly generic method.
[ static ] member object-identifier.method-name<type-parameters> parameter-list [ return-type ] =
method-body

// Explicitly generic class, record, interface, structure,
// or discriminated union.
type type-name<type-parameters> type-definition

備註

明確泛型函式或型別的宣告與非泛型函式或型別的宣告非常類似,除了類型參數的規格(以及使用)之外,在函數或類型名稱之後的角括弧中。

宣告通常是隱含泛型。 如果您未完全指定用來撰寫函式或類型的每個參數類型,編譯程式會嘗試從您撰寫的程式代碼推斷每個參數、值和變數的類型。 如需詳細資訊,請參閱 類型推斷。 如果類型或函式的程式代碼未限制參數的類型,則函式或類型會隱含泛型。 此程式稱為 自動一般化。 自動一般化有一些限制。 例如,如果 F# 編譯程式無法推斷泛型建構的類型,編譯程式會報告參考稱為 值限制的限制的錯誤。 在此情況下,您可能必須新增一些類型批注。 如需自動一般化和值限制的詳細資訊,以及如何變更程式碼以解決問題,請參閱 自動一般化

在上一個語法中, type-parameters 是代表未知類型的逗號分隔參數清單,每個參數都是以單引號開頭,選擇性地使用條件約束子句,進一步限制該類型參數可能使用的類型。 如需各種條件約束子句的語法,以及有關條件約束的其他資訊,請參閱 條件約束

語法中的 類型定義 與非泛型型別的類型定義相同。 它包含類別類型的建構函式參數、選擇性 as 子句、相等符號、記錄欄位、 inherit 子句、歧視聯集的選項, let 以及 do 系結、成員定義,以及非泛型類型定義中允許的任何其他專案。

其他語法元素與非泛型函式和型別的語法元素相同。 例如, 對象識別碼 是代表包含物件本身的標識碼。

屬性、欄位和建構函式不能比封入類型更泛型。 此外,模組中的值不可以是泛型。

隱含泛型建構

當 F# 編譯程式推斷程式代碼中的類型時,它會自動將任何可泛型的函式視為泛型。 如果您明確指定類型,例如參數類型,則會防止自動一般化。

在下列程式代碼範例中, makeList 是泛型,即使它和其參數都未明確宣告為泛型。

let makeList a b = [ a; b ]

函式的簽章推斷為 'a -> 'a -> 'a list。 請注意,在此範例中, ab 會推斷為具有相同類型。 這是因為它們一起包含在清單中,而且清單的所有元素都必須是相同的類型。

您也可以使用類型批注中的單引號語法來表示參數類型是泛型型別參數,讓函式泛型。 在下列程式代碼中, function1 是泛型的,因為其參數是以這種方式宣告為類型參數。

let function1 (x: 'a) (y: 'a) = printfn "%A %A" x y

明確泛型建構

您也可以在角括弧 (<type-parameter>) 中明確宣告其類型參數,讓函式泛型。 下列程式代碼說明這點。

let function2<'T> (x: 'T) (y: 'T) = printfn "%A, %A" x y

使用泛型建構

當您使用泛型函式或方法時,可能不需要指定類型自變數。 編譯程式會使用類型推斷來推斷適當的類型自變數。 如果仍有模棱兩可的情況,您可以在角括弧中提供類型自變數,並以逗號分隔多個類型自變數。

下列程式代碼顯示先前各節中定義的函式用法。

// In this case, the type argument is inferred to be int.
function1 10 20
// In this case, the type argument is float.
function1 10.0 20.0
// Type arguments can be specified, but should only be specified
// if the type parameters are declared explicitly. If specified,
// they have an effect on type inference, so in this example,
// a and b are inferred to have type int.
let function3 a b =
    // The compiler reports a warning:
    function1<int> a b
    // No warning.
    function2<int> a b

備註

有兩種方式可以依名稱參考泛型類型。 例如,list<int>int list 是兩種方式來參考具有單一型別自變數int的泛型型list別。 後者形式通常只搭配 內建 F# 類型使用,例如 listoption。 如果有多個類型自變數,您通常會使用 語法 Dictionary<int, string> ,但也可以使用 語法 (int, string) Dictionary

通配符做為類型自變數

若要指定編譯程式應該推斷類型自變數,您可以使用底線或通配符符號 (_),而不是具名類型自變數。 如下列程式碼所示。

let printSequence (sequence1: Collections.seq<_>) =
    Seq.iter (fun elem -> printf "%s " (elem.ToString())) sequence1

泛型類型和函式中的條件約束

在泛型類型或函式定義中,您只能使用已知可在泛型型別參數上使用的建構。 這是在編譯時期啟用函式和方法呼叫驗證的必要專案。 如果您明確宣告類型參數,您可以將明確條件約束套用至泛型類型參數,以通知編譯程式特定方法和函式可供使用。 不過,如果您允許 F# 編譯程式推斷您的泛型參數類型,它會為您判斷適當的條件約束。 如需詳細資訊,請參閱條件約束

靜態解析的類型參數

F# 程式中有兩種類型參數可供使用。 第一個是上述各節所述的類型泛型型別參數。 這種第一種類型參數相當於在 Visual Basic 和 C# 等語言中使用的泛型型別參數。 另一種類型參數是 F# 特有的,稱為 靜態解析的類型參數。 如需這些建構的相關信息,請參閱 靜態解析類型參數

範例

// A generic function.
// In this example, the generic type parameter 'a makes function3 generic.
let function3 (x: 'a) (y: 'a) = printf "%A %A" x y

// A generic record, with the type parameter in angle brackets.
type GR<'a> = { Field1: 'a; Field2: 'a }

// A generic class.
type C<'a>(a: 'a, b: 'a) =
    let z = a
    let y = b
    member this.GenericMethod(x: 'a) = printfn "%A %A %A" x y z

// A generic discriminated union.
type U<'a> =
    | Choice1 of 'a
    | Choice2 of 'a * 'a

type Test() =
    // A generic member
    member this.Function1<'a>(x, y) = printfn "%A, %A" x y

    // A generic abstract method.
    abstract abstractMethod<'a, 'b> : 'a * 'b -> unit
    override this.abstractMethod<'a, 'b>(x: 'a, y: 'b) = printfn "%A, %A" x y

另請參閱