签名文件包含一组 F# 程序元素(如类型、命名空间和模块)的公共签名的相关信息。 它可用于指定这些程序元素的可访问性。
注解
对于每个 F# 代码文件,可以有一个 签名文件,该文件与代码文件同名,但扩展名为 .fsi 而不是 .fs。 如果直接使用命令行,还可以将签名文件添加到编译命令行。 为了区分代码文件和签名文件,代码文件有时称为 实现文件。 在项目中,签名文件应位于关联的代码文件之前。
签名文件描述相应实现文件中的命名空间、模块、类型和成员。 使用签名文件中的信息可以指定可从实现文件外部的代码以及实现文件内部哪些部分访问相应实现文件中的代码部分。 签名文件中包含的命名空间、模块和类型必须是实现文件中包含的命名空间、模块和类型的子集。 本主题后面记下了一些例外情况,签名文件中未列出的这些语言元素被视为实现文件的专用元素。 如果在项目或命令行中找不到签名文件,则使用默认辅助功能。
有关默认辅助功能的详细信息,请参阅 访问控制。
在签名文件中,不会重复每个方法或函数的类型定义和实现。 而是对每个方法和函数使用签名,该签名充当模块或命名空间片段实现的功能的完整规范。 类型签名的语法与接口和抽象类的抽象方法声明中使用的语法相同,并且由 IntelliSense 和 F# 解释器在显示正确编译的输入时 fsi.exe 显示。
如果类型签名中没有足够的信息来指示类型是否密封,或者它是否为接口类型,则必须将指示该类型的性质的属性添加到编译器。 下表描述了用于此目的的属性。
特征 | DESCRIPTION |
---|---|
[<Sealed>] |
对于没有抽象成员或不应扩展的类型。 |
[<Interface>] |
对于作为接口的类型。 |
如果属性在实现文件中的签名和声明之间不一致,编译器将生成错误。
使用关键字 val
为值或函数值创建签名。 关键字 type
引入了类型签名。
可以使用编译器选项生成签名文件 --sig
。 通常,不手动编写 .fsi 文件。 而是使用编译器生成 .fsi 文件,将其添加到项目(如果有)并通过删除不希望访问的方法和函数进行编辑。
类型签名有以下几种规则:
实现文件中的类型缩写不能与签名文件中没有缩写的类型匹配。
记录和区分联合必须公开其所有字段和构造函数,签名中的顺序必须与实现文件中的顺序匹配。 类可以在签名中显示一些、全部或无其字段和方法。
具有构造函数的类和结构必须公开其基类(声明
inherits
)的声明。 此外,具有构造函数的类和结构必须公开其所有抽象方法和接口声明。接口类型必须显示其所有方法和接口。
值签名的规则如下所示:
用于辅助功能的修饰符(
public
等等internal
)和inline
签名中的修饰mutable
符必须与实现中的修饰符匹配。泛型类型参数的数量(隐式推断或显式声明)必须匹配,泛型类型参数中的类型和类型约束必须匹配。
如果使用属性
Literal
,则它必须同时出现在签名和实现中,并且必须同时对两者使用相同的文本值。签名和实现的参数模式(也称为 arity)必须一致。
如果签名文件中的参数名称与相应的实现文件不同,则会改用签名文件中的名称,这可能会导致调试或分析时出现问题。 如果希望收到此类不匹配的通知,请在项目文件或调用编译器时启用警告 3218(请参阅
--warnon
编译器选项下)。
下面的代码示例演示了包含命名空间、模块、函数值和类型签名以及相应属性的签名文件的示例。 它还显示相应的实现文件。
// Module1.fsi
namespace Library1
module Module1 =
val function1 : int -> int
type Type1 =
new : unit -> Type1
member method1 : unit -> unit
member method2 : unit -> unit
[<Sealed>]
type Type2 =
new : unit -> Type2
member method1 : unit -> unit
member method2 : unit -> unit
[<Interface>]
type InterfaceType1 =
abstract member method1 : int -> int
abstract member method2 : string -> unit
以下代码显示了实现文件。
namespace Library1
module Module1 =
let function1 x = x + 1
type Type1() =
member type1.method1() =
printfn "type1.method1"
member type1.method2() =
printfn "type1.method2"
[<Sealed>]
type Type2() =
member type2.method1() =
printfn "type2.method1"
member type2.method2() =
printfn "type2.method2"
[<Interface>]
type InterfaceType1 =
abstract member method1 : int -> int
abstract member method2 : string -> unit