Note
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de changer d’annuaire.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de changer d’annuaire.
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"