Partilhar via


Genéricos

Os valores de função F#, métodos, propriedades e tipos agregados como classes, registos e sindicatos discriminados podem ser genéricos. As construções genéricas contêm pelo menos um parâmetro do tipo, que é normalmente fornecido pelo utilizador da construção genérica. Funções e tipos genéricos permitem-lhe escrever código que funciona com uma variedade de tipos sem repetir o código para cada tipo. Fazer o seu código genérico pode ser simples em F#, porque muitas vezes o seu código é implicitamente inferido para ser genérico pelos mecanismos de inferência do tipo do compilador e mecanismos automáticos de generalização.

Syntax

// 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, com exceção da especificação (e utilização) dos parâmetros do tipo, nos suportes angulares após a função ou o tipo de nome.

As declarações são muitas vezes implicitamente genéricas. Se não especificar totalmente o tipo de parâmetros utilizados para compor uma função ou tipo, o compilador tenta inferir o tipo de cada parâmetro, valor e variável a partir do código que escreve. Para mais informações, consulte a Inferência tipo. Se o código para o seu tipo ou função não condicionar os tipos de parâmetros, a função ou tipo é implicitamente genérico. Este processo chama-se generalização automática. Há alguns limites para a generalização automática. Por exemplo, se o compilador F# não conseguir inferir os tipos de construção genérica, o compilador relata um erro que se refere a uma restrição chamada restrição de valor. Nesse caso, poderá ter de adicionar algumas anotações tipo. Para obter mais informações sobre a generalização automática e a restrição de valor, e como alterar o seu código para resolver o problema, consulte a Generalização Automática.

Na sintaxe anterior, os parâmetros do tipo são uma lista separada por vírgula de parâmetros que representam tipos desconhecidos, cada um dos quais começa com uma única marca de cotação, opcionalmente com uma cláusula de restrição que limita ainda mais os tipos que podem ser usados para esse parâmetro do tipo. Para a sintaxe para cláusulas de restrição de vários tipos e outras informações sobre constrangimentos, 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. Inclui os parâmetros de construtor para um tipo de classe, uma cláusula opcional as , o símbolo igual, os campos de registo, a inherit cláusula, as escolhas para uma união discriminada, let e do as ligações, definições de membros, e qualquer outra coisa permitida numa definição de tipo não genérico.

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

As propriedades, campos e construtores não podem ser mais genéricos do que o tipo de encerramento. Além disso, os valores num módulo não podem ser genéricos.

Construções Implicitamente Genéricas

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

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

let makeList a b =
    [a; b]

A assinatura da função é deduzida como 'a -> 'a -> 'a list. Note que a e b neste exemplo são deduzidos para ter o mesmo tipo. Isto porque estão incluídos numa lista em conjunto, e todos os elementos de uma lista devem ser do mesmo tipo.

Também pode fazer uma função genérica usando a sintaxe de marca de citação única numa anotação tipo para indicar que um tipo de parâmetro é um parâmetro genérico. No código seguinte, function1 é genérico porque os seus parâmetros são declarados desta forma, como parâmetros de tipo.

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

Construções Explicitamente Genéricas

Também pode fazer uma função genérica declarando explicitamente os seus parâmetros de tipo em suportes angulares (<type-parameter>). O seguinte código ilustra-o.

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

Usando construções genéricas

Quando utilizar funções ou métodos genéricos, poderá não ter de especificar os argumentos do tipo. O compilador utiliza inferência tipo para inferir os argumentos do tipo apropriados. Se ainda houver uma ambiguidade, pode fornecer argumentos de tipo em suportes angulares, separando múltiplos argumentos do tipo com vírgulas.

O código que se segue mostra a utilização das funções definidas nas secçõ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

Nota

Há duas maneiras de se referir a um tipo genérico pelo nome. Por exemplo, list<int> e int list são duas formas de se referir a um tipo list genérico que tem um único argumento inttipo . Este último formulário é convencionalmente utilizado apenas com tipos F# incorporados, tais como list e option. Se existirem vários argumentos do tipo, normalmente utiliza-se a sintaxe Dictionary<int, string> , mas também pode utilizar a sintaxe (int, string) Dictionary.

Wildcards como argumentos de tipo

Para especificar que um argumento de tipo deve ser deduzido pelo compilador, pode utilizar o símbolo de sublinhado ou wildcard (_), em vez de um argumento do tipo nomeado. Isto é mostrado no seguinte código.

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

Constrangimentos em Tipos e Funções Genéricas

Numa definição genérica de tipo ou função, pode utilizar apenas as construções que se sabe estarem disponíveis no parâmetro genérico do tipo. Isto é necessário para permitir a verificação da função e das chamadas de método no momento da compilação. Se declarar explicitamente os parâmetros do seu tipo, pode aplicar uma restrição explícita a um parâmetro genérico do tipo para notificar o compilador de que determinados métodos e funções estão disponíveis. No entanto, se permitir que o compilador F# infera os seus tipos de parâmetros genéricos, determinará as restrições adequadas para si. Para mais informações, consulte Constrangimentos.

Parâmetros de tipo resolvidos estáticamente

Existem dois tipos de parâmetros de tipo que podem ser usados em programas F#. Os primeiros são parâmetros genéricos do tipo descritos nas secções anteriores. Este primeiro tipo de parâmetro do tipo é equivalente aos parâmetros genéricos do tipo que são usados em idiomas como Visual Basic e C#. Outro tipo de parâmetro do tipo é específico de F# e é referido como um parâmetro de tipo estático resolvido. Para obter informações sobre estas construções, consulte parâmetros de tipo estático resolvidos.

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

Ver também