Rozhraní (F#)
Rozhraní určují sady souvisejících členů, které implementují jiné třídy.
Syntaxe
// 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
Poznámky
Deklarace rozhraní se podobají deklaracím tříd s výjimkou toho, že nejsou implementovány žádné členy. Místo toho jsou všechny členy abstraktní, jak je uvedeno klíčovým slovem abstract
. Pro abstraktní metody nezadáte tělo metody. Jazyk F# nemůže definovat výchozí implementaci metody v rozhraní, ale je kompatibilní s výchozími implementacemi definovanými jazykem C#. Výchozí implementace používající default
klíčové slovo jsou podporovány pouze při dědění ze základní třídy bez rozhraní.
Výchozí přístupnost pro rozhraní je public
.
Volitelně můžete každému parametru metody dát název pomocí normální syntaxe jazyka F#:
type ISprintable =
abstract member Print: format: string -> unit
V předchozím ISprintable
příkladu Print
má metoda jeden parametr typu string
s názvem format
.
Existují dva způsoby implementace rozhraní: pomocí výrazů objektů a pomocí typů. V obou případech výraz typu nebo objektu poskytuje těla metody pro abstraktní metody rozhraní. Implementace jsou specifické pro každý typ, který implementuje rozhraní. Metody rozhraní na různých typech se proto můžou navzájem lišit.
Klíčová slova interface
a end
, které označují začátek a konec definice, jsou volitelné, když použijete odlehčenou syntaxi. Pokud tato klíčová slova nepoužíváte, kompilátor se pokusí odvodit, jestli je typ třída nebo rozhraní analýzou konstruktorů, které používáte. Pokud definujete člen nebo používáte jinou syntaxi třídy, typ se interpretuje jako třída.
Styl kódování .NET spočívá ve zahájení všech rozhraní velkým písmenem I
.
Více parametrů můžete zadat dvěma způsoby: F#-style a . Styl NET. Oba se zkompilují stejným způsobem pro uživatele .NET, ale jazyk F#-style vynutí volající jazyka F# používat aplikaci parametrů ve stylu F#a . Styl NET vynutí volající jazyka F# používat aplikaci argumentů s řazenou řazenou kolekcí členů.
type INumericFSharp =
abstract Add: x: int -> y: int -> int
type INumericDotNet =
abstract Add: x: int * y: int -> int
Implementace rozhraní pomocí typů tříd
Jedno nebo více rozhraní v typu třídy můžete implementovat pomocí interface
klíčového slova, názvu rozhraní a klíčového with
slova následovaného definicemi členů rozhraní, jak je znázorněno v následujícím kódu.
type IPrintable =
abstract member Print: unit -> unit
type SomeClass1(x: int, y: float) =
interface IPrintable with
member this.Print() = printfn "%d %f" x y
Implementace rozhraní jsou zděděny, takže žádné odvozené třídy je nemusí reimplementovat.
Volání metod rozhraní
Metody rozhraní lze volat pouze prostřednictvím rozhraní, ne prostřednictvím žádného objektu typu, který implementuje rozhraní. Proto možná budete muset přetypovat na typ rozhraní pomocí :>
operátoru nebo operátoru upcast
, aby bylo možné tyto metody volat.
Chcete-li volat metodu rozhraní, pokud máte objekt typu SomeClass
, je nutné přetypovat objekt na typ rozhraní, jak je znázorněno v následujícím kódu.
let x1 = new SomeClass1(1, 2.0)
(x1 :> IPrintable).Print()
Alternativou je deklarovat metodu na objektu, který upcasts a volá metodu rozhraní, jako v následujícím příkladu.
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()
Implementace rozhraní pomocí výrazů objektů
Výrazy objektů poskytují krátký způsob implementace rozhraní. Jsou užitečné, když nemusíte vytvářet pojmenovaný typ a chcete jen objekt, který podporuje metody rozhraní bez jakýchkoli dalších metod. Výraz objektu je znázorněn v následujícím kódu.
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()
Dědičnost rozhraní
Rozhraní mohou dědit z jednoho nebo více základních rozhraní.
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
Implementace rozhraní s výchozími implementacemi
Jazyk C# podporuje definování rozhraní s výchozími implementacemi, například takto:
using System;
namespace CSharp
{
public interface MyDim
{
public int Z => 0;
}
}
Ty jsou přímo využitelné z jazyka 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}"
Výchozí implementaci můžete přepsat tak override
, že přepíšete libovolný virtuální člen.
Všechny členy v rozhraní, které nemají výchozí implementaci, musí být stále explicitně implementovány.
Implementace stejného rozhraní v různých obecných instancích
Jazyk F# podporuje implementaci stejného rozhraní v různých obecných instancích, například takto:
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"