类型扩展 (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 ]

备注

有两种形式的类型扩展,二者的语法和行为稍有不同。 “内部扩展”是一种与要扩展的类型出现在同一程序集(DLL 或可执行文件)的同一源文件的同一命名空间或模块中的扩展。 “可选扩展”是一种出现在要扩展的类型的原始模块、命名空间或程序集的外部的扩展。 当反射检查类型时,内部扩展会显示在相应的类型上,而可选扩展不会。 可选扩展必须位于模块中,并且只有在打开包含扩展的模块的情况下,它们才会出现在范围之内。

在以前的语法中,typename 表示要扩展的类型。 任何可访问的类型均可以进行扩展,但类型名称必须是实际类型名称,而不是类型缩写。 可以定义一个类型扩展中的多个成员。 self-identifier 表示所调用的对象的实例,就如同在普通成员中一样。

end 关键字在轻量语法中是可选的。

在类型扩展中定义的成员可以像类类型上的其他成员一样使用。 与其他成员相似,它们可以是静态成员或实例成员。 这些方法也称为“扩展方法”;而这些属性也称为“扩展属性”,依此类推。 可选扩展成员将会编译成静态成员,为此,需要将对象实例作为第一个参数隐式传递。 但是,他们会根据自身声明方式的不同而成为实例成员或者静态成员。 包含隐式扩展成员作为类型的成员,并且可以无任何限制地使用这些隐式扩展成员。

扩展方法不能是虚方法或抽象方法。 它们可以重载相同名称的其他方法,但编译器会为不明确调用情况下的非扩展方法赋予优先权。

如果一个类型存在多个内部类型扩展,则所有成员必须是唯一的。 对于可选类型扩展,同一类型的不同类型扩展中的成员可以具有相同的名称。 只有在客户端代码打开两个定义相同成员名称的不同范围时,才会出现多义性错误。

在下面的示例中,模块中的类型具有内部类型扩展。 对于模块外部的客户端代码,类型扩展在任何方面均显示为类型的常规成员。

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"

新实例成员会像 Int32 类型的任何其他成员一样出现在 IntelliSense 中,但仅限包含相应扩展的模块已打开或通过其他方式在范围内的情况。

泛型扩展方法

在 F# 3.1 之前,F# 编译器不支持将带有泛型类型、数组类型、元组类型或 F# 函数类型的 C# 类型扩展方法作为“this”参数使用。 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()

在此代码中,同一个泛型算法代码通过定义一个扩展成员来应用于两种列表,无需使用重载。

请参见

其他资源

F# 语言参考

成员 (F#)