Partilhar via


Interfaces (F#)

As interfaces especificam conjuntos de membros relacionados que outras classes implementam.

Sintaxe

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

Observações

As declarações de interface se assemelham a declarações de classe, exceto que nenhum membro é implementado. Em vez disso, todos os membros são abstratos, como indicado pela palavra-chave abstract. Você não fornece um corpo de método para métodos abstratos. F# não pode definir uma implementação de método padrão em uma interface, mas é compatível com implementações padrão definidas por C#. As implementações padrão que usam a default palavra-chave só são suportadas quando herdadas de uma classe base que não seja de interface.

A acessibilidade padrão para interfaces é public.

Opcionalmente, você pode dar um nome a cada parâmetro de método usando a sintaxe F# normal:

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

No exemplo acima ISprintable , o Print método tem um único parâmetro do tipo string com o nome format.

Há duas maneiras de implementar interfaces: usando expressões de objeto e usando tipos. Em ambos os casos, o tipo ou expressão de objeto fornece corpos de método para métodos abstratos da interface. As implementações são específicas para cada tipo que implementa a interface. Portanto, os métodos de interface em tipos diferentes podem ser diferentes uns dos outros.

As palavras-chave interface e end, que marcam o início e o fim da definição, são opcionais quando você usa sintaxe leve. Se você não usar essas palavras-chave, o compilador tentará inferir se o tipo é uma classe ou uma interface analisando as construções que você usa. Se você definir um membro ou usar outra sintaxe de classe, o tipo será interpretado como uma classe.

O estilo de codificação .NET é iniciar todas as interfaces com maiúscula I.

Você pode especificar vários parâmetros de duas maneiras: estilo F# e . Estilo NET. Ambos serão compilados da mesma maneira para consumidores .NET, mas o estilo F# forçará os chamadores F# a usar o aplicativo de parâmetro no estilo F# e o . NET-style forçará os chamadores F# a usar o aplicativo de argumento tupled.

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

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

Implementando interfaces usando tipos de classe

Você pode implementar uma ou mais interfaces em um tipo de classe usando a palavra-chave interface , o nome da interface e a with palavra-chave, seguidas pelas definições de membro da interface, conforme mostrado no código a seguir.

type IPrintable =
    abstract member Print: unit -> unit

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

As implementações de interface são herdadas, portanto, as classes derivadas não precisam reimplementá-las.

Chamando métodos de interface

Os métodos de interface podem ser chamados apenas através da interface, não através de qualquer objeto do tipo que implementa a interface. Assim, você pode ter que upcast para o tipo de interface usando o :> operador ou o upcast operador para chamar esses métodos.

Para chamar o método de interface quando você tem um objeto do tipo SomeClass, você deve upcast o objeto para o tipo de interface, conforme mostrado no código a seguir.

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

Uma alternativa é declarar um método no objeto que upcast e chama o método de interface, como no exemplo a seguir.

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

Implementando interfaces usando expressões de objeto

As expressões de objeto fornecem uma maneira curta de implementar uma interface. Eles são úteis quando você não precisa criar um tipo nomeado, e você só quer um objeto que suporte os métodos de interface, sem quaisquer métodos adicionais. Uma expressão de objeto é ilustrada no código a seguir.

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

Herança de interface

As interfaces podem herdar de uma ou mais 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

Implementando interfaces com implementações padrão

O C# suporta a definição de interfaces com implementações padrão, assim:

using System;

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

Estes são diretamente consumíveis a 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}"

Você pode substituir uma implementação padrão com override, como substituir qualquer membro virtual.

Todos os membros em uma interface que não têm uma implementação padrão ainda devem ser explicitamente implementados.

Implementando a mesma interface em diferentes instanciações genéricas

O F# suporta a implementação da mesma interface em diferentes instanciações genéricas como:

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"

Consulte também