Compartilhar via


Genéricos

Valores de função F#, métodos, propriedades e tipos de agregação, como classes, registros e uniões discriminadas, podem ser genéricos. Construções genéricas contêm pelo menos um parâmetro de tipo, que geralmente é fornecido pelo usuário do constructo genérico. Funções e tipos genéricos permitem que você escreva um código que funcione com uma variedade de tipos sem repetir o código para cada tipo. Tornar seu código genérico pode ser simples em F#, pois muitas vezes seu código é implicitamente inferido para ser genérico pelos mecanismos de inferência de tipo e generalização automática do compilador.

Sintaxe

// Explicitly generic function.
let function-name<type-parameters> parameter-list =
function-body

// Explicitly generic method.
[ static ] member object-identifier.method-name<type-parameters> parameter-list [ return-type ] =
method-body

// Explicitly generic class, record, interface, structure,
// or discriminated union.
type type-name<type-parameters> type-definition

Observações

A declaração de uma função ou tipo explicitamente genérico é muito semelhante à de uma função ou tipo não genérico, exceto pela especificação (e uso) dos parâmetros de tipo, em colchetes angulares após a função ou o nome do tipo.

As declarações geralmente são implicitamente genéricas. Se você não especificar totalmente o tipo de cada parâmetro usado para compor uma função ou tipo, o compilador tentará inferir o tipo de cada parâmetro, valor e variável do código que você escreve. Para obter mais informações, consulte Type Inference. Se o código de seu tipo ou função não restringir de outra forma os tipos de parâmetros, a função ou o tipo será implicitamente genérico. Esse processo é denominado generalização automática. Há alguns limites na generalização automática. Por exemplo, se o compilador F# não puder inferir os tipos de um constructo genérico, o compilador relatará um erro que se refere a uma restrição chamada restrição de valor. Nesse caso, talvez seja necessário adicionar algumas anotações de tipo. Para obter mais informações sobre a generalização automática e a restrição de valor e como alterar seu código para resolver o problema, consulte Generalização Automática.

Na sintaxe anterior, os parâmetros de tipo são uma lista separada por vírgulas de parâmetros que representam tipos desconhecidos, cada um dos quais começa com uma única aspa, opcionalmente com uma cláusula de restrição que limita ainda mais quais tipos podem ser usados para esse parâmetro de tipo. Para obter a sintaxe de cláusulas de restrição de vários tipos e outras informações sobre restrições, consulte Restrições.

A definição de tipo na sintaxe é a mesma que a definição de tipo para um tipo não genérico. Ele inclui os parâmetros do construtor para um tipo de classe, uma cláusula opcional as , o símbolo igual, os campos de registro, a inherit cláusula, as opções para uma união discriminada e letdo associações, definições de membro e qualquer outra coisa permitida em uma definição de tipo não genérico.

Os outros elementos de sintaxe são os mesmos para funções e tipos não genéricos. Por exemplo, o identificador de objeto é um identificador que representa o próprio objeto que contém.

Propriedades, campos e construtores não podem ser mais genéricos do que o tipo delimitador. Além disso, os valores em um módulo não podem ser genéricos.

Construções implicitamente genéricas

Quando o compilador F# infere os tipos em seu código, ele trata automaticamente qualquer função que possa ser genérica como genérica. Se você especificar um tipo explicitamente, como um tipo de parâmetro, impedirá a generalização automática.

No exemplo de código a seguir, makeList é genérico, embora nem ele nem seus parâmetros sejam explicitamente declarados como genéricos.

let makeList a b = [ a; b ]

A assinatura da função é inferida para ser 'a -> 'a -> 'a list. Observe que a e b neste exemplo são inferidos para ter o mesmo tipo. Isso ocorre porque eles são incluídos em uma lista juntos e todos os elementos de uma lista devem ser do mesmo tipo.

Você também pode tornar uma função genérica usando a sintaxe de marca de aspa única em uma anotação de tipo para indicar que um tipo de parâmetro é um parâmetro de tipo genérico. No código a seguir, function1 é genérico porque seus parâmetros são declarados dessa maneira, como parâmetros de tipo.

let function1 (x: 'a) (y: 'a) = printfn "%A %A" x y

Construções explicitamente genéricas

Você também pode tornar uma função genérica declarando explicitamente seus parâmetros de tipo em colchetes angulares (<type-parameter>). O código a seguir ilustra isso.

let function2<'T> (x: 'T) (y: 'T) = printfn "%A, %A" x y

Usando constructos genéricos

Ao usar funções ou métodos genéricos, talvez não seja necessário especificar os argumentos de tipo. O compilador usa inferência de tipo para inferir os argumentos de tipo apropriados. Se ainda houver uma ambiguidade, você poderá fornecer argumentos de tipo em colchetes angulares, separando vários argumentos de tipo com vírgulas.

O código a seguir mostra o uso das funções definidas nas seções anteriores.

// In this case, the type argument is inferred to be int.
function1 10 20
// In this case, the type argument is float.
function1 10.0 20.0
// Type arguments can be specified, but should only be specified
// if the type parameters are declared explicitly. If specified,
// they have an effect on type inference, so in this example,
// a and b are inferred to have type int.
let function3 a b =
    // The compiler reports a warning:
    function1<int> a b
    // No warning.
    function2<int> a b

Observação

Há duas maneiras de se referir a um tipo genérico por nome. Por exemplo, list<int> e int list são duas maneiras de se referir a um tipo list genérico que tem um argumento intde tipo único. O último formulário é usado convencionalmente apenas com tipos F# internos, como list e option. Se houver vários argumentos de tipo, você normalmente usará a sintaxe Dictionary<int, string> , mas também poderá usar a sintaxe (int, string) Dictionary.

Curingas como argumentos de tipo

Para especificar que um argumento de tipo deve ser inferido pelo compilador, você pode usar o sublinhado ou o símbolo curinga (_), em vez de um argumento de tipo nomeado. Isso é mostrado no código a seguir.

let printSequence (sequence1: Collections.seq<_>) =
    Seq.iter (fun elem -> printf "%s " (elem.ToString())) sequence1

Restrições em tipos e funções genéricos

Em um tipo genérico ou definição de função, você pode usar apenas os constructos que são conhecidos por estarem disponíveis no parâmetro de tipo genérico. Isso é necessário para habilitar a verificação de chamadas de função e método em tempo de compilação. Se você declarar seus parâmetros de tipo explicitamente, poderá aplicar uma restrição explícita a um parâmetro de tipo genérico para notificar o compilador de que determinados métodos e funções estão disponíveis. No entanto, se você permitir que o compilador F# infera seus tipos de parâmetro genéricos, ele determinará as restrições apropriadas para você. Para saber mais, veja Restrições.

Parâmetros de tipo resolvidos estaticamente

Há dois tipos de parâmetros de tipo que podem ser usados em programas F#. O primeiro são parâmetros de tipo genérico do tipo descrito nas seções anteriores. Esse primeiro tipo de parâmetro é equivalente aos parâmetros de tipo genérico usados em linguagens como Visual Basic e C#. Outro tipo de parâmetro de tipo é específico para F# e é conhecido como um parâmetro de tipo estaticamente resolvido. Para obter informações sobre esses constructos, consulte Parâmetros de tipo resolvidos estaticamente.

Exemplos

// A generic function.
// In this example, the generic type parameter 'a makes function3 generic.
let function3 (x: 'a) (y: 'a) = printf "%A %A" x y

// A generic record, with the type parameter in angle brackets.
type GR<'a> = { Field1: 'a; Field2: 'a }

// A generic class.
type C<'a>(a: 'a, b: 'a) =
    let z = a
    let y = b
    member this.GenericMethod(x: 'a) = printfn "%A %A %A" x y z

// A generic discriminated union.
type U<'a> =
    | Choice1 of 'a
    | Choice2 of 'a * 'a

type Test() =
    // A generic member
    member this.Function1<'a>(x, y) = printfn "%A, %A" x y

    // A generic abstract method.
    abstract abstractMethod<'a, 'b> : 'a * 'b -> unit
    override this.abstractMethod<'a, 'b>(x: 'a, y: 'b) = printfn "%A, %A" x y

Consulte também