Generics (F#)

Valori di funzione, metodi, proprietà e tipi aggregati quali classi, record e unioni discriminate di F# possono essere generici. I costrutti generici contengono almeno un parametro di tipo, che in genere viene fornito dall'utente del costrutto generico. Le funzioni e i tipi generici consentono di scrivere codice che funziona con diversi tipi, senza dover ripetere il codice per ogni tipo. Rendere il codice generico può essere semplice in F#, poiché spesso il codice viene implicitamente considerato generico dall'inferenza del tipo del compilatore e dai meccanismi di generalizzazione automatici.

// 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

Note

La dichiarazione di una funzione o di un tipo esplicitamente generico è molto simile a quella di una funzione o un tipo non generico, fatta eccezione per la specifica e l'utilizzo dei parametri di tipo, tra parentesi acute dopo il nome della funzione o del tipo.

Spesso le dichiarazioni sono implicitamente generiche. Se non si specifica interamente il tipo di ogni parametro utilizzato per comporre una funzione o un tipo, il compilatore tenta di dedurre il tipo di ogni parametro, valore e variabile dal codice scritto. Per ulteriori informazioni, vedere Inferenza del tipo (F#). Se il codice per il tipo o la funzione non vincola diversamente i tipi dei parametri, la funzione o il tipo sono implicitamente generici. Questo processo è denominato generalizzazione automatica. La generalizzazione automatica presenta alcuni limiti. Ad esempio, se il compilatore F# non è in grado di dedurre i tipi per un costrutto generico, segnalerà un errore che fa riferimento a una limitazione definita limitazione dei valori. In tal caso, potrebbe essere necessario aggiungere alcune annotazioni di tipo. Per ulteriori informazioni sulla generalizzazione automatica, la limitazione dei valori e le modifiche da apportare al codice per risolvere il problema, vedere Generalizzazione automatica (F#).

Nella sintassi precedente, type-parameters è un elenco di parametri delimitati da virgole che rappresentano tipi sconosciuti, ognuno dei quali inizia con una virgoletta singola, facoltativamente con una clausola di vincolo che pone ulteriori limiti ai tipi utilizzabili per un dato parametro di tipo. Per la sintassi di clausole di vincolo di vari tipi e altre informazioni sui vincoli, vedere Vincoli (F#).

L'oggetto type-definition nella sintassi è uguale alla definizione di tipo per un tipo non generico. Include i parametri del costruttore per un tipo classe, una clausola as facoltativa, il simbolo di uguaglianza, i campi di record, la clausola inherit, le opzioni per un'unione discriminata, le associazioni let e do, le definizioni dei membri e qualunque altro elemento ammesso in una definizione di tipo non generica.

Gli altri elementi della sintassi sono gli stessi delle funzioni e dei tipi non generici. Ad esempio, object-identifier è un identificatore che rappresenta l'oggetto contenitore stesso.

Proprietà, campi e costruttori non possono essere più generici del tipo di inclusione. Inoltre, i valori in un modulo non possono essere generici.

Costrutti implicitamente generici

Quando il compilatore F# deduce i tipi nel codice, considera automaticamente generica qualsiasi funzione che può essere tale. Se si specifica un tipo in modo esplicito, ad esempio un tipo parametro, si impedisce la generalizzazione automatica.

Nell'esempio di codice che segue, makeList è generico, anche se né il tipo né i relativi parametri sono dichiarati esplicitamente come generici.

let makeList a b =
    [a; b]

La firma della funzione dedotta è 'a -> 'a -> 'a list. In questo esempio si deduce che a e b siano dello stesso tipo, in quanto sono inclusi nello stesso elenco e tutti gli elementi di un elenco devono essere dello stesso tipo.

È inoltre possibile rendere generica una funzione utilizzando la sintassi della virgoletta singola in un'annotazione di tipo che indica che un tipo parametro è un parametro di tipo generico. Nel codice che segue, function1 è generico perché i relativi parametri sono dichiarati in questo modo, come parametri di tipo.

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

Costrutti esplicitamente generici

È inoltre possibile rendere generica una funzione dichiarandone in modo esplicito i parametri di tipo in parentesi angolari (< >). Questa condizione è illustrata nel codice che segue.

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

Utilizzo dei costrutti generici

Quando si utilizzano funzioni o metodi generici, potrebbe non essere necessario specificare gli argomenti di tipo. Il compilatore utilizza l'inferenza del tipo per dedurre gli argomenti del tipo appropriati. Se permane una certa ambiguità, è possibile fornire argomenti di tipo tra parentesi acute, separando con virgole i vari argomenti.

Nel codice seguente viene illustrato l'utilizzo delle funzioni definite nelle sezioni precedenti.

// 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

Esistono due modi per fare riferimento a un tipo generico in base al nome. Ad esempio, list<int> e int list sono due modi per fare riferimento a un tipo generico list che dispone di un unico argomento di tipo int. La seconda forma viene comunemente utilizzata solo con i tipi F# incorporati quali list e option. In presenza di più argomenti di tipo, si utilizza in genere la sintassi Dictionary<int, string>, ma è anche possibile utilizzare la sintassi (int, string) Dictionary.

Caratteri jolly come argomenti di tipo

Per specificare che un argomento di tipo deve essere dedotto dal compilatore, è possibile utilizzare il carattere di sottolineatura, o carattere jolly (_), al posto di un argomento di tipo denominato. come illustrato nel codice che segue.

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

Vincoli in funzioni e tipi generici

Nella definizione di una funzione o un tipo generico è possibile utilizzare solo i costrutti notoriamente disponibili nel parametro di tipo generico. Tale restrizione è necessaria per consentire la verifica delle chiamate a funzioni e metodi in fase di compilazione. Se si dichiarano parametri di tipo in modo esplicito, è possibile applicare un vincolo esplicito a un parametro di tipo generico per notificare al compilatore la disponibilità di determinati metodi e funzioni. Se tuttavia si consente al compilatore F# di dedurre i tipi di parametro generici, il compilatore determinerà i vincoli appropriati. Per ulteriori informazioni, vedere Vincoli (F#).

Parametri di tipo risolti staticamente

Esistono due tipi di parametri di tipo che è possibile utilizzare nei programmi F#. Il primo consiste nei parametri di tipo generici descritti nelle sezioni precedenti. Questo genere di parametro di tipo è equivalente ai parametri di tipo generici utilizzati in linguaggi quali Visual Basic e C#. Un altro genere di parametro di tipo è specifico di F# ed è definito parametro di tipo risolto staticamente. Per informazioni su questi costrutti, vedere Parametri di tipo risolti staticamente (F#).

Esempi

// 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

Vedere anche

Riferimenti

Parametri di tipo risolti staticamente (F#)

Vincoli (F#)

Concetti

Generics in .NET Framework

Altre risorse

Riferimenti per il linguaggio F#

Tipi F#

Generalizzazione automatica (F#)