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

其他资源

F# 语言参考