Interfaces (F#)
Las interfaces especifican conjuntos de miembros relacionados que otras clases implementan.
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, excepto que no se implementa ningún miembro. En su lugar, todos los miembros son abstractos, como se indica en la palabra clave abstract
. No se proporciona un cuerpo del 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 al heredar de una clase base que no es de interfaz.
La accesibilidad predeterminada para las interfaces es public
.
Opcionalmente, puede asignar un nombre a cada parámetro de método mediante la sintaxis normal de F#:
type ISprintable =
abstract member Print: format: string -> unit
En el ejemplo anterior ISprintable
, el método Print
tiene un único parámetro del tipo string
con el nombre format
.
Hay dos maneras de implementar interfaces: mediante expresiones de objeto y mediante tipos. En cualquier caso, el tipo o la expresión de objeto proporciona cuerpos de método para 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 diferentes tipos pueden ser diferentes entre sí.
Las palabras clave interface
y end
, que marcan el inicio y el final de la definición, son 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 consiste en comenzar todas las interfaces con una mayúscula I
.
Puede especificar varios parámetros de dos maneras: F#-style y . Estilo NET. Ambos compilarán de la misma manera para los consumidores de .NET, pero F#-style obligará a los llamadores de F# a usar la aplicación de parámetros de estilo F#y . El estilo de NET forzará a los autores de llamadas de F# a usar la aplicación de argumentos de tupla.
type INumericFSharp =
abstract Add: x: int -> y: int -> int
type INumericDotNet =
abstract Add: x: int * y: int -> int
Implementación de interfaces mediante tipos de clase
Puede implementar una o varias interfaces en un tipo de clase mediante la palabra clave interface
, el nombre de la interfaz y la palabra clave with
, seguidas de las definiciones de miembro de la interfaz, como se muestra en el siguiente código.
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.
Métodos de interfaz de llamada
Solo se puede llamar a métodos de interfaz a través de la interfaz, no a través de ningún objeto del tipo que implementa la interfaz. Por lo tanto, es posible que tenga que realizar la difusión al tipo de interfaz mediante el operador :>
o el operador upcast
para llamar a estos métodos.
Para llamar al método de interfaz cuando tiene un objeto de tipo SomeClass
, debe convertir el objeto al tipo de interfaz, como se muestra en el siguiente código.
let x1 = new SomeClass1(1, 2.0)
(x1 :> IPrintable).Print()
Una alternativa es declarar un método en el objeto que realiza la difusión y llama al método de interfaz, como en el siguiente ejemplo.
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()
Implementación de interfaces mediante expresiones de objeto
Las expresiones de objeto proporcionan una forma corta de implementar una interfaz. Son útiles cuando no es necesario crear un tipo con nombre y solo desea un objeto que admita los métodos de interfaz, sin ningún método adicional. En el siguiente código se muestra una expresión de objeto.
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
Una interfaz puede 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, de la siguiente manera:
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 invalidar cualquier miembro virtual.
Los miembros de una interfaz que no tienen una implementación predeterminada deben seguir siendo implementados explícitamente.
Ahora puede implementar la misma interfaz en creaciones de instancias genéricas diferentes
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"