Interfaces (F#)
Las interfaces especifican conjuntos de miembros relacionados que implementan otras clases.
Sintaxis
// 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
Comentarios
Las declaraciones de interfaz son similares a las declaraciones de clase, salvo que no se implementa ningún miembro. En su lugar, todos los miembros son abstractos, como indica la palabra clave abstract . No se proporciona un cuerpo de método para los métodos abstractos. F# no puede definir una implementación de método predeterminada en una interfaz, pero es compatible con las implementaciones predeterminadas definidas por C#. Las implementaciones predeterminadas que usan la palabra clave default solo se admiten cuando se heredan de una clase base que no es de interfaz.
La accesibilidad predeterminada para las interfaces es public .
Opcionalmente, puede dar un nombre a cada parámetro de método mediante la sintaxis de F# normal:
type ISprintable =
abstract member Print : format:string -> unit
En el ejemplo ISprintable anterior, el Print método tiene un único parámetro del tipo con el nombre string format .
Hay dos maneras de implementar interfaces: mediante expresiones de objeto y mediante tipos de clase. En cualquier caso, el tipo de clase u expresión de objeto proporciona cuerpos de método para los métodos abstractos de la interfaz . Las implementaciones son específicas de cada tipo que implementa la interfaz . Por lo tanto, los métodos de interfaz en tipos diferentes pueden ser diferentes entre sí.
Las palabras clave interface y , que marcan el inicio y el final de la definición, son end opcionales cuando se usa la sintaxis ligera. Si no usa estas palabras clave, el compilador intenta deducir si el tipo es una clase o una interfaz mediante el análisis de las construcciones que se usan. Si define un miembro o usa otra sintaxis de clase, el tipo se interpreta como una clase.
El estilo de codificación de .NET es comenzar todas las interfaces con un I mayúscula.
Puede especificar varios parámetros de dos maneras: estilo F# y . Estilo NET. Ambos compilarán de la misma manera para los consumidores de .NET, pero el estilo F# obligará a los llamadores de F# a usar la aplicación de parámetros de estilo F# y . El estilo DE NET obligará a los llamadores de F# a usar la aplicación de argumentos tupled.
type INumericFSharp =
abstract Add: x: int -> y: int -> int
type INumericDotNet =
abstract Add: x: int * y: int -> int
Implementar interfaces mediante tipos de clase
Puede implementar una o varias interfaces en un tipo de clase mediante la palabra clave , el nombre de la interfaz y la palabra clave , seguidas de las definiciones de miembro de interfaz, como se muestra en el interface with código siguiente.
type IPrintable =
abstract member Print : unit -> unit
type SomeClass1(x: int, y: float) =
interface IPrintable with
member this.Print() = printfn "%d %f" x y
Las implementaciones de interfaz se heredan, por lo que las clases derivadas no necesitan volver a implementarlas.
Llamar a métodos de interfaz
Solo se puede llamar a los métodos de interfaz a través de la interfaz , no a través de cualquier objeto del tipo que implementa la interfaz . Por lo tanto, es posible que tenga que convertir al tipo de interfaz mediante el operador o el operador para :> upcast llamar a estos métodos.
Para llamar al método de interfaz cuando tiene un objeto de tipo , debe convertir el objeto al tipo de interfaz, como se muestra SomeClass en el código siguiente.
let x1 = new SomeClass1(1, 2.0)
(x1 :> IPrintable).Print()
Una alternativa consiste en declarar un método en el objeto que se nubla y llama al método de interfaz, como en el ejemplo siguiente.
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()
Implementar interfaces mediante expresiones de objeto
Las expresiones de objeto proporcionan una manera corta de implementar una interfaz. Son útiles cuando no tiene que crear un tipo con nombre y solo quiere un objeto que admita los métodos de interfaz, sin ningún método adicional. Una expresión de objeto se ilustra en el código siguiente.
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()
Herencia de interfaz
Las interfaces pueden heredar de una o varias interfaces 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
Implementación de interfaces con implementaciones predeterminadas
C# admite la definición de interfaces con implementaciones predeterminadas, como:
using System;
namespace CSharp
{
public interface MyDim
{
public int Z => 0;
}
}
Se pueden consumir directamente desde 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}"
Puede invalidar una implementación predeterminada con override , como reemplazar cualquier miembro virtual.
Todos los miembros de una interfaz que no tengan una implementación predeterminada deben implementarse explícitamente.
Implementación de la misma interfaz en diferentes instancias genéricas
F# admite la implementación de la misma interfaz en diferentes instancias genéricas como esta:
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"