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"

Vea también