Interfejsy (F#)
Interfejsy określają zestawy powiązanych elementów członkowskich, które implementują inne klasy.
Składnia
// 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
Uwagi
Deklaracje interfejsu przypominają deklaracje klas, z tą różnicą, że żadne elementy członkowskie nie są implementowane. Zamiast tego wszystkie elementy członkowskie są abstrakcyjne, jak wskazuje słowo kluczowe abstract
. Nie udostępniasz treści metody dla metod abstrakcyjnych. Język F# nie może zdefiniować domyślnej implementacji metody w interfejsie, ale jest zgodny z domyślnymi implementacjami zdefiniowanymi przez język C#. Domyślne implementacje używające słowa kluczowego default
są obsługiwane tylko w przypadku dziedziczenia z klasy bazowej innej niż interfejs.
Domyślnym ułatwieniami dostępu dla interfejsów jest public
.
Opcjonalnie możesz nadać każdemu parametrowi metody nazwę przy użyciu normalnej składni języka F#:
type ISprintable =
abstract member Print: format: string -> unit
W powyższym ISprintable
przykładzie Print
metoda ma jeden parametr typu string
o nazwie format
.
Istnieją dwa sposoby implementowania interfejsów: przy użyciu wyrażeń obiektów i przy użyciu typów. W obu przypadkach wyrażenie typu lub obiektu udostępnia jednostki metody dla abstrakcyjnych metod interfejsu. Implementacje są specyficzne dla każdego typu, który implementuje interfejs. W związku z tym metody interfejsu w różnych typach mogą się różnić od siebie.
Słowa kluczowe interface
i end
, które oznaczają początek i koniec definicji, są opcjonalne podczas używania uproszczonej składni. Jeśli nie używasz tych słów kluczowych, kompilator próbuje wywnioskować, czy typ jest klasą, czy interfejsem, analizując używane konstrukcje. Jeśli zdefiniujesz składową lub użyjesz innej składni klasy, typ jest interpretowany jako klasa.
Styl kodowania platformy .NET to rozpoczęcie wszystkich interfejsów ze stolicą I
.
Można określić wiele parametrów na dwa sposoby: F#-style i . Styl net. Oba te elementy będą kompilować w taki sam sposób dla użytkowników platformy .NET, ale styl F# wymusi użycie wywołań języka F# w celu użycia aplikacji parametrów F#-style i . Styl net wymusi wywołania języka F# do używania aplikacji argumentów tupled.
type INumericFSharp =
abstract Add: x: int -> y: int -> int
type INumericDotNet =
abstract Add: x: int * y: int -> int
Implementowanie interfejsów przy użyciu typów klas
Można zaimplementować jeden lub więcej interfejsów w typie klasy przy użyciu interface
słowa kluczowego, nazwy interfejsu i with
słowa kluczowego, a następnie definicji składowych interfejsu, jak pokazano w poniższym kodzie.
type IPrintable =
abstract member Print: unit -> unit
type SomeClass1(x: int, y: float) =
interface IPrintable with
member this.Print() = printfn "%d %f" x y
Implementacje interfejsu są dziedziczone, więc żadne klasy pochodne nie muszą ich ponownie wdrażać.
Metody wywoływania interfejsu
Metody interfejsu mogą być wywoływane tylko za pośrednictwem interfejsu, a nie za pośrednictwem żadnego obiektu typu, który implementuje interfejs. W związku z tym może być konieczne przesłanie do typu interfejsu przy użyciu :>
operatora lub upcast
operatora w celu wywołania tych metod.
Aby wywołać metodę interfejsu w przypadku obiektu typu SomeClass
, należy przesunąć obiekt do typu interfejsu, jak pokazano w poniższym kodzie.
let x1 = new SomeClass1(1, 2.0)
(x1 :> IPrintable).Print()
Alternatywą jest zadeklarowanie metody na obiekcie, który upcasts i wywołuje metodę interfejsu, jak w poniższym przykładzie.
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()
Implementowanie interfejsów przy użyciu wyrażeń obiektów
Wyrażenia obiektów zapewniają krótki sposób implementowania interfejsu. Są one przydatne, gdy nie musisz tworzyć nazwanego typu i chcesz po prostu obiekt obsługujący metody interfejsu bez żadnych dodatkowych metod. Wyrażenie obiektu zostało zilustrowane w poniższym kodzie.
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()
Dziedziczenie interfejsu
Interfejsy mogą dziedziczyć z co najmniej jednego interfejsu podstawowego.
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
Implementowanie interfejsów z implementacjami domyślnymi
Język C# obsługuje definiowanie interfejsów z domyślnymi implementacjami, w następujący sposób:
using System;
namespace CSharp
{
public interface MyDim
{
public int Z => 0;
}
}
Są one bezpośrednio eksploatacyjne z języka 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}"
Możesz zastąpić domyślną implementację za pomocą override
polecenia , na przykład zastąpić dowolny wirtualny element członkowski.
Wszystkie elementy członkowskie w interfejsie, które nie mają implementacji domyślnej, muszą być nadal jawnie zaimplementowane.
Implementowanie tego samego interfejsu w różnych wystąpieniach ogólnych
Język F# obsługuje implementowanie tego samego interfejsu w różnych ogólnych wystąpieniach, takich jak w następujący sposób:
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"