Interfészek (F#)
Az interfészek olyan kapcsolódó tagcsoportokat határoznak meg, amelyeket más osztályok implementálnak.
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
Megjegyzések
A felületi deklarációk az osztálydeklarációkhoz hasonlítanak, azzal a kivételével, hogy nincsenek tagok implementálva. Ehelyett az összes tag absztrakt, ahogy azt a kulcsszó abstract
is jelzi. Az absztrakt metódusokhoz nem ad meg metódustörzset. Az F# nem tud alapértelmezett metódus-implementációt definiálni egy felületen, de kompatibilis a C# által definiált alapértelmezett implementációkkal. A kulcsszót használó default
alapértelmezett implementációk csak akkor támogatottak, ha nem felületi alaposztályból örökölnek.
A felületek alapértelmezett akadálymentessége a következő public
: .
Az egyes metódusparaméterek megadhatnak egy nevet normál F# szintaxissal:
type ISprintable =
abstract member Print: format: string -> unit
A fenti példában a Print
metódusnak egyetlen paramétere van a típusnak string
a nevévelformat
.ISprintable
Az interfészek kétféleképpen implementálhatók: objektumkifejezések használatával és típusok használatával. A típus- vagy objektumkifejezés mindkét esetben metódustesteket biztosít a felület absztrakt metódusaihoz. Az implementációk minden olyan típusra vonatkoznak, amely implementálja az interfészt. Ezért a különböző típusok felületi metódusai eltérhetnek egymástól.
A definíció kezdetét és végét jelző kulcsszavak interface
end
nem kötelezőek, ha egyszerűsített szintaxist használ. Ha nem használja ezeket a kulcsszavakat, a fordító a használt szerkezetek elemzésével megpróbálja megállapítani, hogy a típus osztály vagy felület-e. Ha tagot határoz meg, vagy más osztályszintaxist használ, a rendszer osztályként értelmezi a típust.
A .NET kódolási stílusa az, hogy az összes felületet nagybetűvel I
kezdje.
Kétféleképpen adhat meg több paramétert: F#-style és . NET-stílus. Mindkettő ugyanúgy fog összeállni a .NET-felhasználók számára, de az F#-stílus kényszeríti az F#-hívókat az F#-stílusú paraméteralkalmazás és a . A NET-stílus kényszeríti az F#-hívókat a csatolt argumentumalkalmazás használatára.
type INumericFSharp =
abstract Add: x: int -> y: int -> int
type INumericDotNet =
abstract Add: x: int * y: int -> int
Interfészek implementálása osztálytípusok használatával
Egy vagy több illesztőt egy osztálytípusban a kulcsszóval, az interface
interfész nevével és a with
kulcsszóval, majd a felülettagdefiníciókkal valósíthat meg, ahogyan az az alábbi kódban is látható.
type IPrintable =
abstract member Print: unit -> unit
type SomeClass1(x: int, y: float) =
interface IPrintable with
member this.Print() = printfn "%d %f" x y
A felületi implementációk öröklődnek, így a származtatott osztályoknak nem kell újrakonfigurálnia őket.
Hívófelület metódusai
Az interfészmetódusok csak az interfészen keresztül hívhatók meg, az interfészt megvalósító objektumon keresztül nem. Így előfordulhat, hogy a metódusok meghívásához az operátort vagy az :>
upcast
operátort kell a felülettípusra felcímkenni.
A felület metódusának meghívásához, ha rendelkezik egy típusú SomeClass
objektummal, az objektumot a felület típusára kell felcímkenni, ahogyan az az alábbi kódban is látható.
let x1 = new SomeClass1(1, 2.0)
(x1 :> IPrintable).Print()
Másik lehetőségként deklarálhat egy metódust azon az objektumon, amely az illesztőmetódusra hivatkozik és meghívja, ahogyan az alábbi példában is látható.
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()
Felületek implementálása objektumkifejezések használatával
Az objektumkifejezések rövid módot biztosítanak egy felület implementálására. Ezek akkor hasznosak, ha nem kell névvel ellátott típust létrehoznia, és csak olyan objektumot szeretne, amely támogatja az interfészmetódusokat további metódusok nélkül. Az objektumkifejezéseket az alábbi kód szemlélteti.
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()
Interfész öröklése
Az illesztők egy vagy több alapillesztőtől örökölhetnek.
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
Interfészek implementálása alapértelmezett implementációkkal
A C# támogatja az alapértelmezett implementációkkal rendelkező felületek meghatározását, például:
using System;
namespace CSharp
{
public interface MyDim
{
public int Z => 0;
}
}
Ezek közvetlenül az F#-ból fogyaszthatók:
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}"
Az alapértelmezett implementációk felülbírálhatók például bármely virtuális tag felülbírálásával override
.
A felület azon tagjait, amelyek nem rendelkeznek alapértelmezett implementációval, explicit módon kell implementálnunk.
Ugyanazon felület implementálása különböző általános példányokon
Az F# támogatja ugyanazt a felületet különböző általános példányokon, például:
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"