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 I
te 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 SomeClass
hebt, 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"