Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Os valores, métodos, propriedades e tipos agregados da função F#, 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 da construção genérica. Funções e tipos genéricos permitem que você escreva código que funciona com uma variedade de tipos sem repetir o código para cada tipo. Tornar seu código genérico pode ser simples em F#, porque muitas vezes seu código é implicitamente inferido como genérico pela inferência de tipo do compilador e mecanismos de generalização automática.
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 para a especificação (e uso) dos parâmetros de tipo, entre colchetes angulares após o nome da função ou do tipo.
As declarações são muitas vezes implicitamente genéricas. Se você não especificar completamente o tipo de cada parâmetro que é usado 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 você escreve. Para obter mais informações, consulte Inferência de tipo. Se o código do seu tipo ou função não restringir os tipos de parâmetros, a função ou tipo é implicitamente genérico. Esse processo é chamado de generalização automática. Existem alguns limites para a generalização automática. Por exemplo, se o compilador F# é incapaz de inferir os tipos para uma construção genérica, o compilador relata 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 o código para resolver o problema, consulte Generalização automática.
Na sintaxe anterior, type-parameters é uma lista separada por vírgulas de parâmetros que representam tipos desconhecidos, cada um dos quais começa com uma única aspas, 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, o símbolo de as igual, os campos de registro, a inherit cláusula, as opções para uma união discriminada e do ligações, let definições de membro e qualquer outra coisa permitida em uma definição de tipo não genérica.
Os outros elementos de sintaxe são os mesmos que os de funções e tipos não genéricos. Por exemplo, object-identifier é 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 de inclusão. 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 como 'a -> 'a -> 'a listsendo . Note 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 aspas simples 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 entre colchetes angulares (<type-parameter>). O código a seguir ilustra isso.
let function2<'T> (x: 'T) (y: 'T) = printfn "%A, %A" x y
Usando construções genéricas
Quando você usa 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ê pode fornecer argumentos de tipo entre 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 pelo nome. Por exemplo, list<int> e int list são duas maneiras de se referir a um tipo list genérico que tem um único argumento intde tipo . A última forma é convencionalmente usada apenas com tipos F# internos, como list e option. Se houver vários argumentos de tipo, você normalmente usa a sintaxe Dictionary<int, string> , mas também pode usar a sintaxe (int, string) Dictionary.
Curingas como argumentos de tipo
Para especificar que um argumento type 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 uma definição genérica de tipo ou função, você pode usar somente as construções que são conhecidas por estarem disponíveis no parâmetro de tipo genérico. Isso é necessário para permitir 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âmetros genéricos, ele determinará as restrições apropriadas para você. Para obter mais informações, consulte Restrições.
Parâmetros de tipo resolvidos estaticamente
Existem dois tipos de parâmetros de tipo que podem ser usados em programas F#. Os primeiros são parâmetros de tipo genéricos do tipo descrito nas seções anteriores. Esse primeiro tipo de parâmetro de tipo é equivalente aos parâmetros de tipo genéricos que são usados em linguagens como Visual Basic e C#. Outro tipo de parâmetro de tipo é específico para F# e é referido como um parâmetro de tipo resolvido estaticamente. Para obter informações sobre essas construções, 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