Partilhar via


Assinaturas

Um arquivo de assinatura contém informações sobre as assinaturas públicas de um conjunto de elementos de programa F#, como tipos, namespaces e módulos. Ele pode ser usado para especificar a acessibilidade desses elementos do programa.

Observações

Para cada arquivo de código F#, você pode ter um arquivo de assinatura, que é um arquivo que tem o mesmo nome do arquivo de código, mas com a extensão .fsi em vez de .fs. Os arquivos de assinatura também podem ser adicionados à linha de comando da compilação se você estiver usando a linha de comando diretamente. Para distinguir entre arquivos de código e arquivos de assinatura, os arquivos de código às vezes são chamados de arquivos de implementação. Em um projeto, o arquivo de assinatura deve preceder o arquivo de código associado.

Um arquivo de assinatura descreve os namespaces, módulos, tipos e membros no arquivo de implementação correspondente. Você usa as informações em um arquivo de assinatura para especificar quais partes do código no arquivo de implementação correspondente podem ser acessadas a partir do código fora do arquivo de implementação e quais partes são internas ao arquivo de implementação. Os namespaces, módulos e tipos incluídos no arquivo de assinatura devem ser um subconjunto dos namespaces, módulos e tipos incluídos no arquivo de implementação. Com algumas exceções observadas posteriormente neste tópico, os elementos de idioma que não estão listados no arquivo de assinatura são considerados privados para o arquivo de implementação. Se nenhum arquivo de assinatura for encontrado no projeto ou na linha de comando, a acessibilidade padrão será usada.

Para obter mais informações sobre a acessibilidade padrão, consulte Controle de acesso.

Em um arquivo de assinatura, você não repete a definição dos tipos e as implementações de cada método ou função. Em vez disso, você usa a assinatura para cada método e função, que atua como uma especificação completa da funcionalidade implementada por um módulo ou fragmento de namespace. A sintaxe para uma assinatura de tipo é a mesma usada em declarações de método abstrato em interfaces e classes abstratas, e também é mostrada pelo IntelliSense e pelo interpretador F# fsi.exe quando exibe a entrada compilada corretamente.

Se não houver informações suficientes na assinatura de tipo para indicar se um tipo está selado ou se é um tipo de interface, você deve adicionar um atributo que indique a natureza do tipo para o compilador. Os atributos que você usa para essa finalidade são descritos na tabela a seguir.

Atributo Description
[<Sealed>] Para um tipo que não tem membros abstratos, ou que não deve ser estendido.
[<Interface>] Para um tipo que é uma interface.

O compilador produz um erro se os atributos não forem consistentes entre a assinatura e a declaração no arquivo de implementação.

Use a palavra-chave val para criar uma assinatura para um valor ou valor de função. A palavra-chave type introduz uma assinatura de tipo.

Você pode gerar um arquivo de assinatura usando a opção de --sig compilador. Geralmente, você não escreve arquivos .fsi manualmente. Em vez disso, você gera arquivos .fsi usando o compilador, adiciona-os ao seu projeto, se tiver um, e edita-os removendo métodos e funções que você não deseja que sejam acessíveis.

Existem várias regras para assinaturas tipográficas:

  • As abreviaturas de tipo em um arquivo de implementação não devem corresponder a um tipo sem uma abreviação em um arquivo de assinatura.

  • Registros e uniões discriminadas devem expor todos ou nenhum de seus campos e construtores, e a ordem na assinatura deve corresponder à ordem no arquivo de implementação. As classes podem revelar alguns, todos ou nenhum de seus campos e métodos na assinatura.

  • Classes e estruturas que têm construtores devem expor as declarações de suas classes base (a inherits declaração). Além disso, as classes e estruturas que têm construtores devem expor todos os seus métodos abstratos e declarações de interface.

  • Os tipos de interface devem revelar todos os seus métodos e interfaces.

As regras para assinaturas de valor são as seguintes:

  • Os modificadores para acessibilidade (public, internal, e assim por diante) e os inline modificadores mutable e na assinatura devem corresponder aos da implementação.

  • O número de parâmetros de tipo genéricos (inferidos implicitamente ou explicitamente declarados) deve corresponder, e os tipos e restrições de tipo em parâmetros de tipo genéricos devem corresponder.

  • Se o Literal atributo for usado, ele deve aparecer na assinatura e na implementação, e o mesmo valor literal deve ser usado para ambos.

  • O padrão de parâmetros (também conhecido como aridade) de assinaturas e implementações deve ser consistente.

  • Se os nomes dos parâmetros em um arquivo de assinatura forem diferentes do arquivo de implementação correspondente, o nome no arquivo de assinatura será usado, o que pode causar problemas ao depurar ou criar o perfil. Se você deseja ser notificado de tais incompatibilidades, ative o aviso 3218 em seu arquivo de projeto ou ao invocar o compilador (consulte --warnon em Opções do compilador).

O exemplo de código a seguir mostra um exemplo de um arquivo de assinatura que tem namespace, módulo, valor de função e assinaturas de tipo juntamente com os atributos apropriados. Ele também mostra o arquivo de implementação correspondente.

// Module1.fsi

namespace Library1
  module Module1 =
    val function1 : int -> int
    type Type1 =
        new : unit -> Type1
        member method1 : unit -> unit
        member method2 : unit -> unit

    [<Sealed>]
    type Type2 =
        new : unit -> Type2
        member method1 : unit -> unit
        member method2 : unit -> unit

    [<Interface>]
    type InterfaceType1 =
        abstract member method1 : int -> int
        abstract member method2 : string -> unit

O código a seguir mostra o arquivo de implementação.

namespace Library1

module Module1 =

    let function1 x = x + 1


    type Type1() =
        member type1.method1() =
            printfn "type1.method1"
        member type1.method2() =
            printfn "type1.method2"


    [<Sealed>]
    type Type2() =
        member type2.method1() =
            printfn "type2.method1"
        member type2.method2() =
            printfn "type2.method2"

    [<Interface>]
    type InterfaceType1 =
        abstract member method1 : int -> int
        abstract member method2 : string -> unit

Consulte também