Compartilhar via


Interfaces (F#)

As interfaces especificam os 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

Comentários

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, conforme indicado pela palavra-chave abstract. Você não fornece um corpo do método para métodos abstratos. O 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 pelo C#. Implementações padrão usando a palavra-chave default só têm suporte ao herdar 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 ISprintable acima, o método Print 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 a 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 os constructos 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 do .NET é iniciar todas as interfaces com um I maiúsculo.

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

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

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

Implementar 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 palavra-chave with, 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, nenhuma classe derivada precisa reimplementá-las.

Chamar métodos de interface

Os métodos de interface só podem ser chamados por meio da interface, não por meio de qualquer objeto do tipo que implementa a interface. Portanto, talvez seja necessário fazer upcast para o tipo de interface usando o operador :> ou o operador upcast para chamar esses métodos.

Para chamar o método de interface quando você tiver um objeto de tipo SomeClass, você deve atualizar 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 ativa 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()

Implementar interfaces usando expressões de objeto

As expressões de objeto fornecem uma maneira breve de implementar uma interface. Elas são úteis quando você não precisa criar um tipo nomeado e só quer um objeto que dê suporte aos métodos de interface, sem nenhum método adicional. 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

Uma interface pode 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

Implementar interfaces com implementações padrão

O C# dá suporte à definição de interfaces com implementações padrão, da seguinte forma:

using System;

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

Elas são diretamente consumidas 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 substituiria qualquer membro virtual.

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

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

O F# dá suporte à implementação da mesma interface em instanciações genéricas diferentes, da seguinte forma:

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"

Confira também