类型扩展 (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 中,但仅限包含相应扩展的模块已打开或通过其他方式在范围内的情况。