Freigeben über


Schnittstellen (F#)

Schnittstellen geben Sätze verwandter Member an, die andere Klassen implementieren.

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

Bemerkungen

Schnittstellendeklarationen ähneln Klassendeklarationen, mit der Ausnahme, dass keine Member implementiert werden. Stattdessen sind alle Elemente abstrakt, wie durch das Schlüsselwort abstractangegeben. Sie stellen keinen Methodentext für abstrakte Methoden bereit. F# kann keine Standardmethodenimplementierung auf einer Schnittstelle definieren, ist aber kompatibel mit Standardimplementierungen, die von C# definiert werden. Standardimplementierungen mit dem default Schlüsselwort werden nur unterstützt, wenn sie von einer Basisklasse ohne Schnittstelle erben.

Die Standardmäßige Barrierefreiheit für Schnittstellen lautet public.

Sie können optional jedem Methodenparameter einen Namen mit der normalen F#-Syntax zuweisen:

type ISprintable =
    abstract member Print: format: string -> unit

Im obigen ISprintable Beispiel weist die Print Methode einen einzelnen Parameter des Typs string mit dem Namen formatauf.

Es gibt zwei Möglichkeiten zum Implementieren von Schnittstellen: mithilfe von Objektausdrücken und mithilfe von Typen. In beiden Fällen stellt der Typ oder der Objektausdruck Methodentexte für abstrakte Methoden der Schnittstelle bereit. Implementierungen sind spezifisch für jeden Typ, der die Schnittstelle implementiert. Daher können Schnittstellenmethoden für verschiedene Typen voneinander abweichen.

Die Schlüsselwörter interface und end, die den Anfang und das Ende der Definition markieren, sind optional, wenn Sie eine einfache Syntax verwenden. Wenn Sie diese Schlüsselwörter nicht verwenden, versucht der Compiler zu ermitteln, ob es sich bei dem Typ um eine Klasse oder eine Schnittstelle handelt, indem die verwendeten Konstrukte analysiert werden. Wenn Sie ein Element definieren oder eine andere Klassensyntax verwenden, wird der Typ als Klasse interpretiert.

Der .NET-Codierungsstil besteht darin, alle Schnittstellen mit einem Großbuchstaben Izu beginnen.

Sie können mehrere Parameter auf zwei Arten angeben: F#-style und . NET-Formatvorlage. Beide kompilieren die gleiche Methode für .NET-Consumer, aber F#-Style erzwingt F#-Aufrufer zur Verwendung der F#-Formatparameteranwendung und . Im NET-Stil wird erzwungen, dass F#-Aufrufer tuplierte Argumentanwendung verwenden.

type INumericFSharp =
    abstract Add: x: int -> y: int -> int

type INumericDotNet =
    abstract Add: x: int * y: int -> int

Implementieren von Schnittstellen mithilfe von Klassentypen

Sie können eine oder mehrere Schnittstellen in einem Klassentyp implementieren, indem Sie das interface Schlüsselwort, den Namen der Schnittstelle und das with Schlüsselwort verwenden, gefolgt von den Schnittstellenmememmdefinitionen, wie im folgenden Code gezeigt.

type IPrintable =
    abstract member Print: unit -> unit

type SomeClass1(x: int, y: float) =
    interface IPrintable with
        member this.Print() = printfn "%d %f" x y

Schnittstellenimplementierungen werden geerbt, sodass alle abgeleiteten Klassen sie nicht erneut implementieren müssen.

Definieren leerer oder Markerschnittstellen

Leere Schnittstellen, auch als Markerschnittstellen bezeichnet, können verwendet werden, um einen Satz von Typen zu identifizieren, ohne dass ein bestimmtes Verhalten erforderlich ist. Diese Schnittstellen haben keine Member und dienen als Möglichkeit zum Markieren oder Kategorisieren von Typen für Kategorisierungszwecke.

Sie definieren eine leere Schnittstelle mithilfe der interface end Syntax:

// Define an empty interface (also known as a marker interface)
type IMarker = 
    interface end

// Implement the empty interface in a record type
type MyRecord = 
    { Name: string }
    interface IMarker

// Implement the empty interface in a class type
type MyClass(value: int) =
    member _.Value = value
    interface IMarker

Im obigen IMarker Beispiel wird als leere Schnittstelle definiert. Beide MyRecord und MyClass implementieren diese Schnittstelle, ohne Methodenimplementierungen bereitstellen zu müssen, da die Schnittstelle keine Member enthält. Auf diese Weise können Sie den Schnittstellentyp verwenden, um diese Typen auf gemeinsame Weise zu identifizieren und zu bearbeiten.

Aufrufen von Schnittstellenmethoden

Schnittstellenmethoden können nur über die Schnittstelle aufgerufen werden, nicht über ein Objekt des Typs, der die Schnittstelle implementiert. Daher müssen Sie möglicherweise den Schnittstellentyp mithilfe des :> Operators oder des upcast Operators aufsteigen, um diese Methoden aufzurufen.

Um die Schnittstellenmethode aufzurufen, wenn Sie über ein Objekt vom Typ verfügen SomeClass, müssen Sie das Objekt auf den Schnittstellentyp hochcasten, wie im folgenden Code gezeigt.

let x1 = new SomeClass1(1, 2.0)
(x1 :> IPrintable).Print()

Eine Alternative besteht darin, eine Methode für das Objekt zu deklarieren, das die Schnittstellenmethode hochcasts und aufruft, wie im folgenden Beispiel gezeigt.

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()

Implementieren von Schnittstellen mithilfe von Objektausdrücken

Objektausdrücke bieten eine kurze Möglichkeit zum Implementieren einer Schnittstelle. Sie sind nützlich, wenn Sie keinen benannten Typ erstellen müssen, und Sie möchten nur ein Objekt, das die Schnittstellenmethoden unterstützt, ohne zusätzliche Methoden. Ein Objektausdruck wird im folgenden Code veranschaulicht.

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()

Schnittstellenvererbung

Schnittstellen können von einer oder mehreren Basisschnittstellen erben.

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

Implementieren von Schnittstellen mit Standardimplementierungen

C# unterstützt das Definieren von Schnittstellen mit Standardimplementierungen, z. B.:

using System;

namespace CSharp
{
    public interface MyDim
    {
        public int Z => 0;
    }
}

Diese sind direkt von F# konsumierbar:

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}"

Sie können eine Standardimplementierung mit overrideeinem beliebigen virtuellen Element außer Kraft setzen.

Alle Member in einer Schnittstelle, die nicht über eine Standardimplementierung verfügen, müssen weiterhin explizit implementiert werden.

Implementieren derselben Schnittstelle bei verschiedenen generischen Instanziierungen

F# unterstützt die Implementierung derselben Schnittstelle bei verschiedenen generischen Instanziierungen wie der folgenden:

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"

Siehe auch