Gränssnitt (F#)
Gränssnitt anger uppsättningar med relaterade medlemmar som andra klasser implementerar.
Syntax
// 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
Kommentarer
Gränssnittsdeklarationer liknar klassdeklarationer förutom att inga medlemmar implementeras. I stället är alla medlemmar abstrakta, vilket anges med nyckelordet abstract
. Du anger inte någon metodtext för abstrakta metoder. F# kan inte definiera en standardmetodimplementering i ett gränssnitt, men den är kompatibel med standardimplementeringar som definieras av C#. Standardimplementeringar med nyckelordet default
stöds endast när du ärver från en basklass som inte är gränssnitt.
Standardtillgängligheten för gränssnitt är public
.
Du kan också ge varje metodparameter ett namn med normal F#-syntax:
type ISprintable =
abstract member Print: format: string -> unit
I exemplet ovan ISprintable
Print
har metoden en enda parameter av typen string
med namnet format
.
Det finns två sätt att implementera gränssnitt: med hjälp av objektuttryck och med hjälp av typer. I båda fallen tillhandahåller typ- eller objektuttrycket metodkroppar för abstrakta metoder i gränssnittet. Implementeringar är specifika för varje typ som implementerar gränssnittet. Därför kan gränssnittsmetoder på olika typer skilja sig från varandra.
Nyckelorden interface
och end
, som markerar början och slutet av definitionen, är valfria när du använder enkel syntax. Om du inte använder dessa nyckelord försöker kompilatorn härleda om typen är en klass eller ett gränssnitt genom att analysera de konstruktioner som du använder. Om du definierar en medlem eller använder annan klasssyntax tolkas typen som en klass.
.NET-kodningsformatet är att börja alla gränssnitt med versalt I
.
Du kan ange flera parametrar på två sätt: F#-style och . NET-format. Båda kompileras på samma sätt för .NET-konsumenter, men F#-style tvingar F#-anropare att använda parameterprogrammet F#-style och . NET-format tvingar F#-anropare att använda tupled-argumentprogram.
type INumericFSharp =
abstract Add: x: int -> y: int -> int
type INumericDotNet =
abstract Add: x: int * y: int -> int
Implementera gränssnitt med hjälp av klasstyper
Du kan implementera ett eller flera gränssnitt i en klasstyp med hjälp av nyckelordet interface
, namnet på gränssnittet och nyckelordet with
följt av gränssnittsmedlemsdefinitionerna enligt följande kod.
type IPrintable =
abstract member Print: unit -> unit
type SomeClass1(x: int, y: float) =
interface IPrintable with
member this.Print() = printfn "%d %f" x y
Gränssnittsimplementeringar ärvs, så alla härledda klasser behöver inte omimplementeras.
Anropa gränssnittsmetoder
Gränssnittsmetoder kan bara anropas via gränssnittet, inte via något objekt av den typ som implementerar gränssnittet. Därför kan du behöva upparbeta till gränssnittstypen med hjälp av operatorn :>
eller operatorn upcast
för att kunna anropa dessa metoder.
Om du vill anropa gränssnittsmetoden när du har ett objekt av typen SomeClass
måste du skicka objektet till gränssnittstypen enligt följande kod.
let x1 = new SomeClass1(1, 2.0)
(x1 :> IPrintable).Print()
Ett alternativ är att deklarera en metod för objektet som upcasts och anropar gränssnittsmetoden, som i följande exempel.
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()
Implementera gränssnitt med hjälp av objektuttryck
Objektuttryck är ett kort sätt att implementera ett gränssnitt. De är användbara när du inte behöver skapa en namngiven typ, och du bara vill ha ett objekt som stöder gränssnittsmetoderna, utan några ytterligare metoder. Ett objektuttryck illustreras i följande kod.
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()
Gränssnittsarv
Gränssnitt kan ärva från ett eller flera basgränssnitt.
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
Implementera gränssnitt med standardimplementeringar
C# stöder definition av gränssnitt med standardimplementeringar, så här:
using System;
namespace CSharp
{
public interface MyDim
{
public int Z => 0;
}
}
Dessa är direkt förbrukningsbara från 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}"
Du kan åsidosätta en standardimplementering med override
, som att åsidosätta alla virtuella medlemmar.
Alla medlemmar i ett gränssnitt som inte har någon standardimplementering måste fortfarande implementeras uttryckligen.
Implementera samma gränssnitt på olika allmänna instansier
F# stöder implementering av samma gränssnitt på olika allmänna instansier så här:
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"