Dela via


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 ISprintablePrint 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 SomeClassmå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"

Se även