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"

Viz také