泛 型

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# 编译器无法推断泛型构造的类型,编译器将报告一个错误,该错误引用了称为 值限制的限制。 在这种情况下,可能需要添加一些类型批注。 有关自动通用化和值限制的详细信息,以及如何更改代码以解决该问题,请参阅 自动通用化

在前面的语法中, 类型参数 是表示未知类型的参数的逗号分隔列表,每个参数都以单引号开头,可以选择使用约束子句进一步限制可用于该类型参数的类型。 有关各种约束子句和其他有关约束的信息的语法,请参阅 约束

语法中的 类型定义 与非泛型类型的类型定义相同。 它包括类类型的构造函数参数、可选 as 子句、等号、记录字段、子句、 inherit 区分联合 letdo 的选项以及绑定、成员定义以及非泛型类型定义中允许的任何其他内容。

其他语法元素与非泛型函数和类型的语法元素相同。 例如, 对象标识符 是表示包含对象本身的标识符。

属性、字段和构造函数不能比封闭类型更通用。 此外,模块中的值不能是泛型的。

隐式泛型构造

当 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

另请参阅