Interfaces (F#)

Interfaces geven sets met gerelateerde leden op die door andere klassen worden geïmplementeerd.

Syntaxis

// 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

Opmerkingen

Interfacedeclaraties lijken op klassedeclaraties, behalve dat er geen leden zijn geïmplementeerd. In plaats daarvan zijn alle leden abstract, zoals aangegeven door het trefwoord abstract. U geeft geen hoofdtekst van de methode op voor abstracte methoden. F# kan geen standaardmethode-implementatie definiëren op een interface, maar is compatibel met standaard implementaties die zijn gedefinieerd door C#. Standaard implementaties die gebruikmaken van het default trefwoord worden alleen ondersteund wanneer ze worden overgenomen van een basisklasse die niet van de interface is.

De standaardtoegankelijkheid voor interfaces is public.

U kunt desgewenst elke methodeparameter een naam geven met behulp van de normale F#-syntaxis:

type ISprintable =
    abstract member Print: format: string -> unit

In het bovenstaande ISprintable voorbeeld heeft de Print methode één parameter van het type string met de naam format.

Er zijn twee manieren om interfaces te implementeren: met behulp van objectexpressies en met behulp van typen. In beide gevallen biedt het type of de objectexpressie methodeteksten voor abstracte methoden van de interface. Implementaties zijn specifiek voor elk type dat de interface implementeert. Daarom kunnen interfacemethoden voor verschillende typen verschillen van elkaar.

De trefwoorden interface en end, die het begin en einde van de definitie markeren, zijn optioneel wanneer u lichtgewicht syntaxis gebruikt. Als u deze trefwoorden niet gebruikt, probeert de compiler af te stellen of het type een klasse of een interface is door de constructies te analyseren die u gebruikt. Als u een lid definieert of andere klassesyntaxis gebruikt, wordt het type geïnterpreteerd als een klasse.

De .NET-coderingsstijl is om alle interfaces met een hoofdletter Ite beginnen.

U kunt op twee manieren meerdere parameters opgeven: F#-stijl en . NET-stijl. Beide worden op dezelfde manier gecompileerd voor .NET-consumenten, maar F#-stijl dwingt F#-aanroepers af om F#-stijl parametertoepassing te gebruiken en . Net-stijl dwingt F#-bellers af om tupled argumenttoepassing te gebruiken.

type INumericFSharp =
    abstract Add: x: int -> y: int -> int

type INumericDotNet =
    abstract Add: x: int * y: int -> int

Interfaces implementeren met klassetypen

U kunt een of meer interfaces in een klassetype implementeren met behulp van het interface trefwoord, de naam van de interface en het with trefwoord, gevolgd door de definities van interfaceleden, zoals wordt weergegeven in de volgende code.

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-implementaties worden overgenomen, dus hoeven eventuele afgeleide klassen deze niet opnieuw te implementeren.

Methoden voor aanroepende interface

Interfacemethoden kunnen alleen worden aangeroepen via de interface, niet via een object van het type dat de interface implementeert. Daarom moet u het interfacetype mogelijk upcasten met behulp van de :> operator of de upcast operator om deze methoden aan te roepen.

Als u de interfacemethode wilt aanroepen wanneer u een object van het type SomeClasshebt, moet u het object upcasten naar het interfacetype, zoals wordt weergegeven in de volgende code.

let x1 = new SomeClass1(1, 2.0)
(x1 :> IPrintable).Print()

U kunt ook een methode declareren voor het object dat de interfacemethode upcastt en aanroept, zoals in het volgende voorbeeld.

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()

Interfaces implementeren met objectexpressies

Objectexpressies bieden een korte manier om een interface te implementeren. Ze zijn handig wanneer u geen benoemd type hoeft te maken en u alleen een object wilt dat ondersteuning biedt voor de interfacemethoden, zonder extra methoden. Een objectexpressie wordt geïllustreerd in de volgende code.

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()

Overname van interface

Interfaces kunnen overnemen van een of meer basisinterfaces.

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

Interfaces implementeren met standaard implementaties

C# ondersteunt het definiëren van interfaces met standaard implementaties, zoals:

using System;

namespace CSharp
{
    public interface MyDim
    {
        public int Z => 0;
    }
}

Deze zijn rechtstreeks te gebruiken vanuit 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}"

U kunt een standaard implementatie overschrijven met override, zoals het overschrijven van elk virtueel lid.

Alle leden in een interface die geen standaard implementatie hebben, moeten nog steeds expliciet worden geïmplementeerd.

Dezelfde interface implementeren bij verschillende algemene instantiëringen

F# biedt ondersteuning voor het implementeren van dezelfde interface bij verschillende algemene instantiëringen, zoals:

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"

Zie ook