Genéricos (F#)
A função F# valores, métodos, propriedades e agregar tipos como classes, registros e uniões discriminadas podem ser genérico. Construções genéricas contenham pelo menos um parâmetro de tipo, o que é geralmente fornecido pelo usuário da construção genérico. Tipos e funções genéricas permitem que você escrever código que funciona com uma variedade de tipos sem repetir o código para cada tipo. Tornando o código genérico pode ser simple em F#, porque geralmente seu código é implicitamente inferido para ser genérico, inferência de tipos do compilador e mecanismos de generalização automática.
// Explicitly generic function.
let function-name<type-parameters> parameter-list =
function-body
// Explicitly generic method.
[ static ] member object-identifer.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
Comentários
A declaração de uma função genérica explicitamente ou o tipo é semelhante a de uma função de não-genéricas ou o tipo, exceto para a especificação (e o uso) os parâmetros de tipo, colchetes angulares após o nome de função ou tipo.
Declarações são freqüentemente genéricas implicitamente. Se você não especificar o tipo de cada parâmetro que é usado para compor uma função ou um tipo totalmente, o compilador tenta inferir o tipo de cada parâmetro, o valor e a variável a partir do código que você escreve. Para obter mais informações, consulte Inferência de tipo (F#). Se o código para o seu tipo ou a função não restrinja os tipos de parâmetros caso contrário, a função ou tipo é implicitamente genérico. Esse processo é chamado de automática generalização. Existem alguns limites na generalização automática. Por exemplo, se o compilador F# não é possível inferir os tipos para uma construção genérico, o compilador reporta um erro que se refere a uma restrição chamada a a restrição de valor. Nesse caso, talvez você precise adicionar algumas anotações de tipo. Para obter mais informações sobre como alterar o seu código para resolver o problema, a restrição de valor e generalização automática, consulte Generalização automática (F#).
Na sintaxe anterior, type-parameters é uma lista separada por vírgulas que representam tipos desconhecidos, cada qual inicia com uma aspa, opcionalmente com uma cláusula constraint adicional que limita os tipos de parâmetros que pode ser usada para esse tipo de parâmetro. Para obter a sintaxe para cláusulas de restrição de vários tipos e outras informações sobre as restrições, consulte Restrições (F#).
O type-definition 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, um recurso opcional as cláusula, o símbolo de igual, os campos do registros, o inherit cláusula, as opções para uma união discriminada, let e do ligações, definições de membro e nada mais permitido em uma definição de tipo não genérico.
Os outros elementos de sintaxe são as mesmas para tipos e funções de não-genéricas. Por exemplo, object-identifier é um identificador que representa o próprio objeto contendo.
Construtores, campos e propriedades não podem ser mais genéricos que o tipo de 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 no seu código, ele automaticamente trata qualquer função que pode ser genérica como genéricos. Se você especificar um tipo explicitamente, como, por exemplo, um tipo de parâmetro, você pode evitar generalização automática.
No exemplo de código a seguir, makeList é genérico, embora nem ele nem seus parâmetros declarados explicitamente como genérico.
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 têm 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 fazer uma função genérica usando a sintaxe das 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 fazer uma função genérica declarando explicitamente seus tipos de parâmetros em colchetes angulares (< >). O código a seguir ilustra isso.
let function2<'T> x y =
printfn "%A, %A" x y
Usar genérico construções.
Quando você usa métodos ou funções genéricas, talvez não tenha que especificar os argumentos de tipo. O compilador usa a inferência de tipos para inferir os argumentos de tipo apropriado. Se ainda existe uma ambigüidade, você pode fornecer os 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 que são definidos 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 duas maneiras para se referir a um tipo genérico list que tem um argumento de tipo único int. O último formulário é usado 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 você também pode usar a sintaxe (int, string) Dictionary. |
Caracteres curinga como argumentos de tipo
Para especificar que um argumento de tipo deve ser inferido pelo compilador, você pode usar o sublinhado ou um símbolo de caractere curinga (_), em vez de um argumento de tipo nomeado. Isto é mostrado no código a seguir.
let printSequence (sequence1: Collections.seq<_>) =
Seq.iter (fun elem -> printf "%s " (elem.ToString())) sequence1
Restrições em tipos genéricos e funções
Em um tipo genérico ou definição de função, você pode usar apenas essas construções são conhecidas por estar disponível no parâmetro de tipo genérico. Isso é necessário para ativar a verificação de chamadas de função e o método em tempo de compilação. Se você declarar explicitamente os parâmetros de tipo, você pode aplicar uma restrição explícita para um parâmetro de tipo genérico para notificar o compilador a certas funções e métodos estão disponíveis. No entanto, se você permitir que o compilador inferir os tipos de parâmetro genérico F#, ele determinará as restrições apropriadas para você. Para obter mais informações, consulte Restrições (F#).
Resolvido estaticamente os parâmetros de tipo
Existem dois tipos de parâmetros de tipo que podem ser usados em programas de F#. A primeira são os parâmetros de tipo genérico do tipo descrito nas seções anteriores. Este primeiro tipo de parâmetro de tipo é o equivalente para os parâmetros de tipo genérico que são usados em linguagens como, por exemplo, Visual Basic e C#. Outro tipo de parâmetro de tipo é específico para F# e é conhecido como um resolvido estaticamente o parâmetro de tipo. Para obter informações sobre essas construções, consulte Resolvido estaticamente os parâmetros de tipo (F#).
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
Referência
Resolvido estaticamente os parâmetros de tipo (F#)
Conceitos
Classes genéricas no .NET Framework