类型扩展 (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()
在此代码中,同一个泛型算法代码通过定义一个扩展成员来应用于两种列表,无需使用重载。