Udostępnij za pośrednictwem


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ą overridepolecenia , 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"

Zobacz też