可变类型

可变类型注释指示参数、变量或值具有与所指定类型兼容的类型,其兼容性由面向对象的类或接口层次结构中的位置确定。 当未在类型层次结构中自动转换为更高级的类型,但你仍希望使功能能够与层次结构中的任意类型或实现接口的任何类型配合使用时,可变类型特别有用。

语法

#type

备注

在前面的语法中,type 表示基类型或接口。

可变类型等效于具有约束的泛型类型,该约束将允许的类型限制为与基类型或接口类型兼容的类型。 也就是说,以下两行代码是等效的。

#SomeType

'T when 'T :> SomeType

可变类型在几种情况下都很有用。 例如,当你有一个高阶函数(以一个函数作为参数的函数)时,让函数返回可变类型通常很有用。 在以下示例中,在 iterate2 中使用带有序列参数的可变类型使高阶函数能够与生成序列、数组、列表和任何其他可枚举类型的函数一起使用。

请考虑以下两个函数,其中一个返回一个序列,另一个返回一个灵活类型。

let iterate1 (f : unit -> seq<int>) =
    for e in f() do printfn "%d" e
let iterate2 (f : unit -> #seq<int>) =
    for e in f() do printfn "%d" e

// Passing a function that takes a list requires a cast.
iterate1 (fun () -> [1] :> seq<int>)

// Passing a function that takes a list to the version that specifies a
// flexible type as the return value is OK as is.
iterate2 (fun () -> [1])

另举一例,请考虑 Seq.concat 库函数:

val concat: sequences:seq<#seq<'T>> -> seq<'T>

可以将以下任何可枚举序列传递给此函数:

  • 列表列表
  • 数组列表
  • 列表数组
  • 序列数组
  • 可枚举序列的任何其他组合

以下代码使用 Seq.concat 来演示你可以通过使用灵活类型来支持的场景。

let list1 = [1;2;3]
let list2 = [4;5;6]
let list3 = [7;8;9]

let concat1 = Seq.concat [ list1; list2; list3]
printfn "%A" concat1

let array1 = [|1;2;3|]
let array2 = [|4;5;6|]
let array3 = [|7;8;9|]

let concat2 = Seq.concat [ array1; array2; array3 ]
printfn "%A" concat2

let concat3 = Seq.concat [| list1; list2; list3 |]
printfn "%A" concat3

let concat4 = Seq.concat [| array1; array2; array3 |]
printfn "%A" concat4

let seq1 = { 1 .. 3 }
let seq2 = { 4 .. 6 }
let seq3 = { 7 .. 9 }

let concat5 = Seq.concat [| seq1; seq2; seq3 |]

printfn "%A" concat5

输出如下所示。

seq [1; 2; 3; 4; ...]
seq [1; 2; 3; 4; ...]
seq [1; 2; 3; 4; ...]
seq [1; 2; 3; 4; ...]
seq [1; 2; 3; 4; ...]

在 F# 中,与其他面向对象的语言一样,在某些上下文中,派生类型或实现接口的类型会自动转换为基类型或接口类型。 这些自动转换发生在直接参数中,但在类型处于从属位置、作为更复杂类型(如函数类型的返回类型)的一部分或作为类型参数时不会发生。 因此,可变类型表示法主要适用于要向其应用该表示法的类型属于更复杂类型的情况。

另请参阅