接口 指定其他类实现的相关成员集。
语法
// Interface declaration:
[ attributes ]
type [accessibility-modifier] interface-name =
[ interface ] [ inherit base-interface-name ...]
abstract member1 : [ argument-types1 -> ] return-type1
abstract member2 : [ argument-types2 -> ] return-type2
...
[ end ]
// Implementing, inside a class type definition:
interface interface-name with
member self-identifier.member1argument-list = method-body1
member self-identifier.member2argument-list = method-body2
// Implementing, by using an object expression:
[ attributes ]
let class-name (argument-list) =
{ new interface-name with
member self-identifier.member1argument-list = method-body1
member self-identifier.member2argument-list = method-body2
[ base-interface-definitions ]
}
member-list
注解
接口声明类似于类声明,但未实现任何成员。 相反,所有成员都是抽象的,如关键字 abstract
所示。 不为抽象方法提供方法主体。 F# 不能在接口上定义默认方法实现,但它与 C# 定义的默认实现兼容。 仅当从非接口基类继承时,才支持使用 default
关键字的默认实现。
接口的默认可访问性为 public
。
可以选择使用普通 F# 语法为每个方法参数提供一个名称:
type ISprintable =
abstract member Print: format: string -> unit
在上面的ISprintable
示例中,Print
该方法具有具有名称format
的类型string
的单一参数。
可通过两种方法实现接口:使用对象表达式和类型。 在任一情况下,类型或对象表达式都为接口的抽象方法提供方法主体。 实现特定于实现接口的每个类型。 因此,不同类型的接口方法可能彼此不同。
使用轻型语法时,关键字和end
标记定义开始和结尾的关键字interface
是可选的。 如果不使用这些关键字,编译器会尝试通过分析所使用的构造来推断类型是类还是接口。 如果定义成员或使用其他类语法,类型将解释为类。
.NET 编码样式是开始所有具有大写 I
接口的接口。
可以通过两种方式指定多个参数:F#样式和 。NET 样式。 这两者都会为 .NET 使用者编译相同的方法,但 F#-style 将强制 F# 调用方使用 F#样式参数应用程序和 。NET 样式将强制 F# 调用方使用 tupled 参数应用程序。
type INumericFSharp =
abstract Add: x: int -> y: int -> int
type INumericDotNet =
abstract Add: x: int * y: int -> int
使用类类型实现接口
可以使用关键字、接口with
名称和关键字(后跟接口成员定义)在类类型interface
中实现一个或多个接口,如以下代码所示。
type IPrintable =
abstract member Print: unit -> unit
type SomeClass1(x: int, y: float) =
interface IPrintable with
member this.Print() = printfn "%d %f" x y
接口实现是继承的,因此任何派生类都不需要重新实现它们。
调用接口方法
接口方法只能通过接口调用,而不能通过实现接口的类型的任何对象调用。 因此,可能需要使用 :>
运算符或 upcast
运算符向上转换到接口类型才能调用这些方法。
若要在具有类型 SomeClass
对象时调用接口方法,必须将对象向上转换为接口类型,如以下代码所示。
let x1 = new SomeClass1(1, 2.0)
(x1 :> IPrintable).Print()
另一种方法是在对象上声明一个方法,该方法会向上转换并调用接口方法,如以下示例所示。
type SomeClass2(x: int, y: float) =
member this.Print() = (this :> IPrintable).Print()
interface IPrintable with
member this.Print() = printfn "%d %f" x y
let x2 = new SomeClass2(1, 2.0)
x2.Print()
使用对象表达式实现接口
对象表达式提供了实现接口的简短方法。 当无需创建命名类型,并且只需要支持接口方法的对象,而无需任何其他方法时,它们非常有用。 以下代码演示了对象表达式。
let makePrintable (x: int, y: float) =
{ new IPrintable with
member this.Print() = printfn "%d %f" x y }
let x3 = makePrintable (1, 2.0)
x3.Print()
接口继承
接口可以从一个或多个基接口继承。
type Interface1 =
abstract member Method1: int -> int
type Interface2 =
abstract member Method2: int -> int
type Interface3 =
inherit Interface1
inherit Interface2
abstract member Method3: int -> int
type MyClass() =
interface Interface3 with
member this.Method1(n) = 2 * n
member this.Method2(n) = n + 100
member this.Method3(n) = n / 10
使用默认实现实现实现接口
C# 支持使用默认实现定义接口,如下所示:
using System;
namespace CSharp
{
public interface MyDim
{
public int Z => 0;
}
}
这些项直接从 F# 中消耗:
open CSharp
// You can implement the interface via a class
type MyType() =
member _.M() = ()
interface MyDim
let md = MyType() :> MyDim
printfn $"DIM from C#: %d{md.Z}"
// You can also implement it via an object expression
let md' = { new MyDim }
printfn $"DIM from C# but via Object Expression: %d{md'.Z}"
可以重写默认实现 override
,例如重写任何虚拟成员。
接口中没有默认实现的任何成员仍必须显式实现。
在不同的泛型实例化中实现同一接口
F# 支持在不同的泛型实例中实现相同的接口,如下所示:
type IA<'T> =
abstract member Get : unit -> 'T
type MyClass() =
interface IA<int> with
member x.Get() = 1
interface IA<string> with
member x.Get() = "hello"
let mc = MyClass()
let iaInt = mc :> IA<int>
let iaString = mc :> IA<string>
iaInt.Get() // 1
iaString.Get() // "hello"