Vincoli
In questo argomento vengono descritti i vincoli che è possibile applicare ai parametri di tipo generico per specificare i requisiti per un argomento di tipo in un tipo o una funzione generica.
Sintassi
type-parameter-list when constraint1 [ and constraint2]
Osservazioni:
Esistono diversi vincoli che è possibile applicare per limitare i tipi che possono essere usati in un tipo generico. Nella tabella seguente sono elencati e descritti questi vincoli.
Vincolo | Sintassi | Descrizione |
---|---|---|
Vincolo di tipo | type-parameter :>type | Il tipo specificato deve essere uguale o derivato dal tipo specificato oppure, se il tipo è un'interfaccia, il tipo specificato deve implementare l'interfaccia. |
Vincolo Null | type-parameter : null | Il tipo specificato deve supportare il valore letterale Null. Sono inclusi tutti i tipi di oggetto .NET, ma non l'elenco F#, la tupla, la funzione, la classe, il record o i tipi di unione. |
Vincolo membro esplicito | [(]type-parameter [o ... o type-parameter)] : (firma membro) | Almeno uno degli argomenti di tipo forniti deve avere un membro con la firma specificata; non destinato all'uso comune. I membri devono essere definiti in modo esplicito sul tipo o parte di un'estensione di tipo implicito per essere destinazioni valide per un vincolo di membro esplicito. |
Vincolo del costruttore | type-parameter : ( new : unit -> 'a ) | Il tipo specificato deve avere un costruttore senza parametri. |
Vincolo tipo valore | type-parameter : struct | Il tipo specificato deve essere un tipo di valore .NET. |
Vincolo di tipo riferimento | type-parameter : non struct | Il tipo specificato deve essere un tipo di riferimento .NET. |
Vincolo del tipo di enumerazione | type-parameter : enumerazioni<di tipo sottostante> | Il tipo specificato deve essere un tipo enumerato con il tipo sottostante specificato; non destinato all'uso comune. |
Vincolo delegato | type-parameter : delegate<tuple-parameter-type, return-type> | Il tipo specificato deve essere un tipo delegato con gli argomenti specificati e il valore restituito; non destinato all'uso comune. |
Vincolo di confronto | type-parameter : confronto | Il tipo specificato deve supportare il confronto. |
Vincolo di uguaglianza | type-parameter : uguaglianza | Il tipo specificato deve supportare l'uguaglianza. |
Vincolo non gestito | type-parameter : non gestito | Il tipo specificato deve essere un tipo non gestito. I tipi non gestiti sono determinati tipi primitivi (sbyte , byte , nativeint float32 unativeint float uint32 int32 int16 char int64 uint64 uint16 o decimal ), tipi di enumerazione, nativeptr<_> , o una struttura non generica i cui campi sono tutti tipi non gestiti. |
È necessario aggiungere un vincolo quando il codice deve usare una funzionalità disponibile per il tipo di vincolo, ma non per i tipi in generale. Ad esempio, se si usa il vincolo di tipo per specificare un tipo di classe, è possibile usare uno dei metodi di tale classe nella funzione o nel tipo generico.
La specifica dei vincoli è talvolta necessaria quando si scrivono parametri di tipo in modo esplicito, perché senza un vincolo, il compilatore non ha modo di verificare che le funzionalità in uso saranno disponibili in qualsiasi tipo che potrebbe essere fornito in fase di esecuzione per il parametro di tipo.
I vincoli più comuni usati nel codice F# sono vincoli di tipo che specificano classi o interfacce di base. Gli altri vincoli vengono usati dalla libreria F# per implementare determinate funzionalità, ad esempio il vincolo membro esplicito, che viene usato per implementare l'overload degli operatori per gli operatori aritmetici o vengono forniti principalmente perché F# supporta il set completo di vincoli supportati da Common Language Runtime.
Durante il processo di inferenza del tipo, alcuni vincoli vengono dedotti automaticamente dal compilatore. Ad esempio, se si usa l'operatore +
in una funzione, il compilatore deduce un vincolo membro esplicito sui tipi di variabile usati nell'espressione.
Il codice seguente illustra alcune dichiarazioni di vincolo:
// Base Type Constraint
type Class1<'T when 'T :> System.Exception> =
class end
// Interface Type Constraint
type Class2<'T when 'T :> System.IComparable> =
class end
// Null constraint
type Class3<'T when 'T : null> =
class end
// Member constraint with instance member
type Class5<'T when 'T : (member Method1 : 'T -> int)> =
class end
// Member constraint with property
type Class6<'T when 'T : (member Property1 : int)> =
class end
// Constructor constraint
type Class7<'T when 'T : (new : unit -> 'T)>() =
member val Field = new 'T()
// Reference type constraint
type Class8<'T when 'T : not struct> =
class end
// Enumeration constraint with underlying value specified
type Class9<'T when 'T : enum<uint32>> =
class end
// 'T must implement IComparable, or be an array type with comparable
// elements, or be System.IntPtr or System.UIntPtr. Also, 'T must not have
// the NoComparison attribute.
type Class10<'T when 'T : comparison> =
class end
// 'T must support equality. This is true for any type that does not
// have the NoEquality attribute.
type Class11<'T when 'T : equality> =
class end
type Class12<'T when 'T : delegate<obj * System.EventArgs, unit>> =
class end
type Class13<'T when 'T : unmanaged> =
class end
// Member constraints with two type parameters
// Most often used with static type parameters in inline functions
let inline add(value1 : ^T when ^T : (static member (+) : ^T * ^T -> ^T), value2: ^T) =
value1 + value2
// ^T and ^U must support operator +
let inline heterogenousAdd(value1 : ^T when (^T or ^U) : (static member (+) : ^T * ^U -> ^T), value2 : ^U) =
value1 + value2
// If there are multiple constraints, use the and keyword to separate them.
type Class14<'T,'U when 'T : equality and 'U : equality> =
class end