Partager via


Interfaces (F#)

Les interfaces spécifient des ensembles de membres associés implémentés par d’autres classes.

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

Remarques

Les déclarations d’interface ressemblent à des déclarations de classe, sauf qu’aucun membre n’est implémenté. Au lieu de cela, tous les membres sont abstraits, comme indiqué par le mot clé abstract. Vous ne fournissez pas de corps de méthode pour les méthodes abstraites. F# ne peut pas définir une implémentation de méthode par défaut sur une interface, mais elle est compatible avec les implémentations par défaut définies par C#. Les implémentations par défaut utilisant le default mot clé sont uniquement prises en charge lors de l’héritage d’une classe de base non-interface.

L’accessibilité par défaut pour les interfaces est public.

Vous pouvez éventuellement donner à chaque paramètre de méthode un nom à l’aide de la syntaxe F# normale :

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

Dans l’exemple ci-dessus ISprintable , la Print méthode a un paramètre unique du type string portant le nom format.

Il existe deux façons d’implémenter des interfaces : à l’aide d’expressions d’objet et à l’aide de types. Dans les deux cas, le type ou l’expression d’objet fournit des corps de méthode pour les méthodes abstraites de l’interface. Les implémentations sont spécifiques à chaque type qui implémente l’interface. Par conséquent, les méthodes d’interface sur différents types peuvent être différentes les unes des autres.

Les mots clés interface et end, qui marquent le début et la fin de la définition, sont facultatifs lorsque vous utilisez une syntaxe légère. Si vous n’utilisez pas ces mots clés, le compilateur tente de déduire si le type est une classe ou une interface en analysant les constructions que vous utilisez. Si vous définissez un membre ou utilisez une autre syntaxe de classe, le type est interprété comme une classe.

Le style de codage .NET consiste à commencer toutes les interfaces avec une majuscule I.

Vous pouvez spécifier plusieurs paramètres de deux façons : F#-style et . Style NET. Les deux compilent la même façon pour les consommateurs .NET, mais le style F#force les appelants F# à utiliser l’application de paramètre F#-style et . Le style NET force les appelants F# à utiliser l’application d’argument tupled.

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

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

Implémentation d’interfaces à l’aide de types de classes

Vous pouvez implémenter une ou plusieurs interfaces dans un type de classe à l’aide du interface mot clé, du nom de l’interface et du with mot clé, suivi des définitions des membres de l’interface, comme indiqué dans le code suivant.

type IPrintable =
    abstract member Print: unit -> unit

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

Les implémentations d’interface sont héritées. Par conséquent, toutes les classes dérivées n’ont pas besoin de les réexémettre.

Définir des interfaces vides ou de marqueurs

Les interfaces vides, également appelées interfaces de marqueur, peuvent être utilisées pour identifier un ensemble de types sans nécessiter de comportement spécifique. Ces interfaces n’ont aucun membre et servent de moyen de marquer ou de marquer des types de balises à des fins de catégorisation.

Vous définissez une interface vide à l’aide de la interface end syntaxe :

// 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

Dans l’exemple ci-dessus, IMarker est défini comme une interface vide. À la fois MyRecord et MyClass implémentez cette interface sans avoir à fournir d’implémentations de méthode, car l’interface n’a aucun membre. Cela vous permet d’utiliser le type d’interface pour identifier et utiliser ces types de manière courante.

Méthodes d’interface d’appel

Les méthodes d’interface ne peuvent être appelées qu’à l’aide de l’interface, et non via un objet du type qui implémente l’interface. Par conséquent, vous devrez peut-être effectuer une mise en ligne vers le type d’interface à l’aide de l’opérateur :> ou de l’opérateur upcast pour appeler ces méthodes.

Pour appeler la méthode d’interface lorsque vous avez un objet de type SomeClass, vous devez upcastr l’objet sur le type d’interface, comme indiqué dans le code suivant.

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

Une alternative consiste à déclarer une méthode sur l’objet qui upcasts et appelle la méthode d’interface, comme dans l’exemple suivant.

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

Implémentation d’interfaces à l’aide d’expressions d’objet

Les expressions d’objet fournissent un moyen court d’implémenter une interface. Ils sont utiles quand vous n’avez pas besoin de créer un type nommé et que vous voulez simplement un objet qui prend en charge les méthodes d’interface, sans aucune méthode supplémentaire. Une expression d’objet est illustrée dans le code suivant.

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

Héritage de l’interface

Les interfaces peuvent hériter d’une ou plusieurs interfaces de base.

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

Implémentation d’interfaces avec des implémentations par défaut

C# prend en charge la définition d’interfaces avec des implémentations par défaut, comme suit :

using System;

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

Ceux-ci sont directement consommables à partir de 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}"

Vous pouvez remplacer une implémentation par défaut avec override, comme remplacer n’importe quel membre virtuel.

Tous les membres d’une interface qui n’ont pas d’implémentation par défaut doivent toujours être implémentés explicitement.

Implémentation de la même interface à différentes instanciations génériques

F# prend en charge l’implémentation de la même interface à différentes instanciations génériques comme suit :

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"

Voir aussi