Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
F#-funktionsvärden, metoder, egenskaper och aggregeringstyper som klasser, poster och diskriminerade fackföreningar kan vara generiska. Generiska konstruktioner innehåller minst en typparameter, som vanligtvis tillhandahålls av användaren av den generiska konstruktionen. Med allmänna funktioner och typer kan du skriva kod som fungerar med en mängd olika typer utan att upprepa koden för varje typ. Att göra koden generisk kan vara enkel i F#, eftersom koden ofta implicit härleds till att vara generisk av kompilatorns typinferens och automatiska generaliseringsmekanismer.
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
Anmärkningar
Deklarationen av en explicit generisk funktion eller typ liknar ungefär den för en icke-generisk funktion eller typ, förutom specifikationen (och användningen) av typparametrarna, i vinkelparenteser efter funktionen eller typnamnet.
Deklarationer är ofta implicit allmänna. Om du inte helt anger vilken typ av parameter som används för att skapa en funktion eller typ försöker kompilatorn härleda typen av varje parameter, värde och variabel från koden du skriver. Mer information finns i Typ slutsatsdragning. Om koden för din typ eller funktion inte på annat sätt begränsar typerna av parametrar är funktionen eller typen implicit allmän. Den här processen heter automatisk generalisering. Det finns vissa begränsningar för automatisk generalisering. Om F#-kompilatorn till exempel inte kan härleda typerna för en allmän konstruktion rapporterar kompilatorn ett fel som refererar till en begränsning som kallas värdebegränsning. I så fall kan du behöva lägga till vissa typanteckningar. Mer information om automatisk generalisering och värdebegränsning och hur du ändrar koden för att lösa problemet finns i Automatisk generalisering.
I den tidigare syntaxen är typparametrar en kommaavgränsad lista med parametrar som representerar okända typer, som var och en börjar med ett enda citattecken, eventuellt med en begränsningssats som ytterligare begränsar vilka typer som kan användas för den typparametern. Syntaxen för villkorssatser av olika slag och annan information om begränsningar finns i Begränsningar.
Typdefinitionen i syntaxen är samma som typdefinitionen för en icke-generisk typ. Den innehåller konstruktorparametrarna för en klasstyp, en valfri as sats, likhetssymbolen, postfälten inherit , -satsen, valen för en diskriminerad union let och do bindningar, medlemsdefinitioner och allt annat som tillåts i en icke-generisk typdefinition.
De andra syntaxelementen är samma som för icke-generiska funktioner och typer. Objektidentifierare är till exempel en identifierare som representerar själva det innehållande objektet.
Egenskaper, fält och konstruktorer kan inte vara mer generiska än omslutningstypen. Dessutom kan värden i en modul inte vara generiska.
Implicit allmänna konstruktioner
När F#-kompilatorn härleder typerna i koden behandlar den automatiskt alla funktioner som kan vara generiska som generiska. Om du uttryckligen anger en typ, till exempel en parametertyp, förhindrar du automatisk generalisering.
I följande kodexempel makeList är det allmänt, även om varken det eller dess parametrar uttryckligen deklareras som generiska.
let makeList a b = [ a; b ]
Funktionens signatur härleds till 'a -> 'a -> 'a list. Observera att a och b i det här exemplet härleds att ha samma typ. Det beror på att de ingår i en lista tillsammans, och alla element i en lista måste vara av samma typ.
Du kan också göra en funktion generisk genom att använda syntaxen för enkla citattecken i en typanteckning för att ange att en parametertyp är en allmän typparameter. I följande kod function1 är allmän eftersom dess parametrar deklareras på det här sättet som typparametrar.
let function1 (x: 'a) (y: 'a) = printfn "%A %A" x y
Explicit generiska konstruktioner
Du kan också göra en funktion generisk genom att uttryckligen deklarera dess typparametrar inom vinkelparenteser (<type-parameter>). Följande kod illustrerar detta.
let function2<'T> (x: 'T) (y: 'T) = printfn "%A, %A" x y
Använda allmänna konstruktioner
När du använder allmänna funktioner eller metoder kanske du inte behöver ange typargumenten. Kompilatorn använder typinferens för att härleda lämpliga typargument. Om det fortfarande finns en tvetydighet kan du ange typargument inom vinkelparenteser och separera flera typargument med kommatecken.
Följande kod visar användningen av de funktioner som definieras i föregående avsnitt.
// 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
Anmärkning
Det finns två sätt att referera till en allmän typ efter namn. Och är till exempel int listlist<int> två sätt att referera till en allmän typ list som har ett argument intav typen . Det senare formuläret används endast konventionellt med inbyggda F#-typer som list och option. Om det finns flera typargument använder du normalt syntaxen Dictionary<int, string> , men du kan också använda syntaxen (int, string) Dictionary.
Jokertecken som typargument
Om du vill ange att ett typargument ska härledas av kompilatorn kan du använda understrecket eller jokertecknet (_) i stället för ett namngivet typargument. Detta visas i följande kod.
let printSequence (sequence1: Collections.seq<_>) =
Seq.iter (fun elem -> printf "%s " (elem.ToString())) sequence1
Begränsningar i allmänna typer och funktioner
I en allmän typ eller funktionsdefinition kan du bara använda de konstruktioner som är kända för att vara tillgängliga för den generiska typparametern. Detta krävs för att aktivera verifiering av funktions- och metodanrop vid kompileringstillfället. Om du deklarerar dina typparametrar explicit kan du använda en explicit begränsning för en allmän typparameter för att meddela kompilatorn att vissa metoder och funktioner är tillgängliga. Men om du tillåter att F#-kompilatorn härleder dina allmänna parametertyper avgör den lämpliga begränsningar för dig. Mer information finns i Begränsningar.
Statiskt lösta typparametrar
Det finns två typer av typparametrar som kan användas i F#-program. Den första är generiska typparametrar av den typ som beskrivs i föregående avsnitt. Den här första typen av typparameter motsvarar de allmänna typparametrar som används på språk som Visual Basic och C#. En annan typ av typparameter är specifik för F# och kallas för en statiskt löst typparameter. Information om dessa konstruktioner finns i Statiskt lösta typparametrar.
Exempel
// 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