Generics
F#-functiewaarden, methoden, eigenschappen en statistische typen, zoals klassen, records en gediscrimineerde samenvoegingen, kunnen algemeen zijn. Algemene constructies bevatten ten minste één typeparameter, die meestal wordt geleverd door de gebruiker van de algemene constructie. Met algemene functies en typen kunt u code schrijven die werkt met verschillende typen zonder de code voor elk type te herhalen. Het maken van uw code algemeen kan eenvoudig zijn in F#, omdat uw code vaak impliciet wordt afgeleid van de typedeductie en automatische generalisatiemechanismen van de compiler.
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
Opmerkingen
De declaratie van een expliciet generieke functie of type lijkt veel op die van een niet-algemene functie of type, met uitzondering van de specificatie (en het gebruik) van de typeparameters, tussen punthaken na de functie of typenaam.
Declaraties zijn vaak impliciet algemeen. Als u niet volledig het type van elke parameter opgeeft die wordt gebruikt voor het opstellen van een functie of type, probeert de compiler het type van elke parameter, waarde en variabele af te stellen van de code die u schrijft. Zie Type deductie voor meer informatie. Als de code voor uw type of functie de typen parameters niet anders beperkt, is de functie of het type impliciet algemeen. Dit proces heet automatische generalisatie. Er gelden enkele limieten voor automatische generalisatie. Als de F#-compiler bijvoorbeeld de typen voor een algemene constructie niet kan afleiden, meldt de compiler een fout die verwijst naar een beperking die de waardebeperking wordt genoemd. In dat geval moet u mogelijk enkele typeaantekeningen toevoegen. Zie Automatische generalisatie voor meer informatie over automatische generalisatie en de waardebeperking en het wijzigen van uw code om het probleem op te lossen.
In de vorige syntaxis is type-parameters een door komma's gescheiden lijst met parameters die onbekende typen vertegenwoordigen, die elk beginnen met één aanhalingsteken, optioneel met een beperkingscomponent die verder beperkt welke typen kunnen worden gebruikt voor die typeparameter. Zie Beperkingen voor de syntaxis voor beperkingsclausules van verschillende soorten en andere informatie over beperkingen.
De typedefinitie in de syntaxis is hetzelfde als de typedefinitie voor een niet-algemeen type. Het bevat de constructorparameters voor een klassetype, een optionele as
component, het gelijke symbool, de recordvelden, de inherit
component, de keuzes voor een gediscrimineerde samenvoeging en do
bindingen, let
liddefinities en alles wat is toegestaan in een niet-algemene typedefinitie.
De andere syntaxiselementen zijn hetzelfde als die voor niet-algemene functies en typen. Object-id is bijvoorbeeld een id die het object zelf vertegenwoordigt.
Eigenschappen, velden en constructors kunnen niet algemener zijn dan het type omsluiten. Waarden in een module kunnen ook niet algemeen zijn.
Impliciet algemene constructies
Wanneer de F#-compiler de typen in uw code afgeeft, wordt elke functie die algemeen als algemeen kan worden beschouwd, automatisch behandeld. Als u een type expliciet opgeeft, zoals een parametertype, voorkomt u automatische generalisatie.
In het volgende codevoorbeeld makeList
is dit algemeen, ook al worden deze noch de parameters expliciet gedeclareerd als algemeen.
let makeList a b =
[a; b]
De handtekening van de functie wordt afgeleid.'a -> 'a -> 'a list
Houd er rekening mee dat en b
in dit voorbeeld worden afgeleid dat a
ze hetzelfde type hebben. Dit komt doordat ze in een lijst samen zijn opgenomen en alle elementen van een lijst van hetzelfde type moeten zijn.
U kunt ook een functie algemeen maken met behulp van de syntaxis van één aanhalingsteken in een typeaantekening om aan te geven dat een parametertype een algemene typeparameter is. In de volgende code is algemeen omdat function1
de parameters op deze manier worden gedeclareerd, als typeparameters.
let function1 (x: 'a) (y: 'a) =
printfn "%A %A" x y
Expliciet algemene constructies
U kunt een functie ook algemeen maken door expliciet de typeparameters in hoekhaken (<type-parameter>
) te declareren. De volgende code illustreert dit.
let function2<'T> (x: 'T) (y: 'T) =
printfn "%A, %A" x y
Algemene constructies gebruiken
Wanneer u algemene functies of methoden gebruikt, hoeft u mogelijk niet de typeargumenten op te geven. De compiler gebruikt typedeductie om de juiste typeargumenten af te trekken. Als er nog steeds dubbelzinnigheid is, kunt u typeargumenten opgeven tussen punthaken en meerdere typeargumenten scheiden met komma's.
In de volgende code ziet u het gebruik van de functies die in de vorige secties zijn gedefinieerd.
// 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
Notitie
Er zijn twee manieren om te verwijzen naar een algemeen type op naam. En zijn bijvoorbeeld list<int>
twee manieren om te verwijzen naar een algemeen type list
met één typeargumentint
.int list
De laatste vorm wordt conventioneel alleen gebruikt met ingebouwde F#-typen zoals list
en option
. Als er meerdere typeargumenten zijn, gebruikt u normaal gesproken de syntaxis Dictionary<int, string>
, maar kunt u ook de syntaxis (int, string) Dictionary
gebruiken.
Jokertekens als typeargumenten
Als u wilt opgeven dat een typeargument moet worden afgeleid door de compiler, kunt u het onderstrepingsteken of jokerteken (_
) gebruiken in plaats van een benoemd typeargument. Dit wordt weergegeven in de volgende code.
let printSequence (sequence1: Collections.seq<_>) =
Seq.iter (fun elem -> printf "%s " (elem.ToString())) sequence1
Beperkingen in algemene typen en functies
In een algemene type- of functiedefinitie kunt u alleen de constructies gebruiken die bekend zijn als beschikbaar zijn voor de algemene typeparameter. Dit is vereist om de verificatie van functie- en methode-aanroepen tijdens het compileren in te schakelen. Als u uw typeparameters expliciet declareert, kunt u een expliciete beperking toepassen op een algemene typeparameter om de compiler op de hoogte te stellen dat bepaalde methoden en functies beschikbaar zijn. Als u de F#-compiler echter toestaat om uw algemene parametertypen af te leiden, wordt de juiste beperkingen voor u bepaald. Zie Beperkingen voor meer informatie.
Parameters voor statisch opgelost type
Er zijn twee soorten typeparameters die kunnen worden gebruikt in F#-programma's. De eerste zijn algemene typeparameters van het type dat in de vorige secties wordt beschreven. Dit eerste type parameter is gelijk aan de algemene typeparameters die worden gebruikt in talen zoals Visual Basic en C#. Een ander type parameter is specifiek voor F# en wordt aangeduid als een statisch opgeloste typeparameter. Zie Parameters voor statisch opgelost type voor meer informatie over deze constructies.
Voorbeelden
// 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