介面 會指定其他類別實作的相關成員集合。
語法
// 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,方法具有名稱 為的型stringformat別單一參數。
有兩種方式可以實作介面:使用對象表達式,以及使用型別。 不論是哪一種情況,類型或物件表達式都提供介面抽象方法的方法主體。 實作是實作 介面的每個類型特有的。 因此,不同類型上的介面方法可能彼此不同。
當您使用輕量型語法時,標記定義開頭和結尾的關鍵詞 interface 和 end是選擇性的。 如果您不使用這些關鍵詞,編譯程式會嘗試藉由分析您使用的建構來推斷類型是類別或介面。 如果您定義成員或使用其他類別語法,則類型會解譯為類別。
.NET 程式代碼撰寫樣式是以大寫 I開頭的所有介面。
您可以透過兩種方式指定多個參數:F#樣式和 。NET 樣式。 這兩者都會針對 .NET 取用者編譯相同的方式,但 F#-style 會強制 F# 呼叫者使用 F#-style 參數應用程式和 。NET 樣式會強制 F# 呼叫端使用 Tupled 自變數應用程式。
type INumericFSharp =
abstract Add: x: int -> y: int -> int
type INumericDotNet =
abstract Add: x: int * y: int -> int
使用類別類型實作介面
您可以使用 關鍵詞、介面的名稱和 interface 關鍵詞,後面接著介面成員定義,在類別類型with中實作一或多個介面,如下列程式代碼所示。
type IPrintable =
abstract member Print: unit -> unit
type SomeClass1(x: int, y: float) =
interface IPrintable with
member this.Print() = printfn "%d %f" x y
介面實作是繼承的,因此任何衍生類別都不需要重新實作它們。
定義空白或標記介面
空介面,也稱為標記介面,可用來識別一組類型,而不需要任何特定行為。 這些介面沒有成員,可作為標記或標記類型以進行分類的方式。
您可以使用以下 interface end 語法定義空白介面:
// Define an empty interface (also known as a marker interface)
type IMarker =
interface end
// Implement the empty interface in a record type
type MyRecord =
{ Name: string }
interface IMarker
// Implement the empty interface in a class type
type MyClass(value: int) =
member _.Value = value
interface IMarker
在上面的範例中, IMarker 定義為空介面。 兩者都 MyRecord 實 MyClass 作此介面,而不需要提供任何方法實作,因為介面沒有成員。 這可讓您使用介面類型,以一般方式識別及使用這些類型。
呼叫介面方法
介面方法只能透過 介面呼叫,而不能透過實作介面之型別的任何物件呼叫。 因此,您可能必須使用 運算符或 :> 運算符來向上轉換至介面類型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"