Parameters en argumenten
In dit onderwerp wordt taalondersteuning beschreven voor het definiëren van parameters en het doorgeven van argumenten aan functies, methoden en eigenschappen. Het bevat informatie over het doorgeven van verwijzingen en het definiëren en gebruiken van methoden die een variabel aantal argumenten kunnen aannemen.
Parameters en argumenten
De termparameter wordt gebruikt om de namen te beschrijven voor waarden die naar verwachting worden opgegeven. Het termargument wordt gebruikt voor de waarden die zijn opgegeven voor elke parameter.
Parameters kunnen worden opgegeven in tupel- of curriede vorm, of in een combinatie van de twee. U kunt argumenten doorgeven met behulp van een expliciete parameternaam. Parameters van methoden kunnen worden opgegeven als optioneel en gegeven een standaardwaarde.
Parameterpatronen
Parameters die worden geleverd aan functies en methoden, worden in het algemeen gescheiden door spaties. Dit betekent dat in principe een van de patronen die worden beschreven in Match Expressions , kan worden gebruikt in een parameterlijst voor een functie of lid.
Methoden gebruiken meestal de tuple-vorm van het doorgeven van argumenten. Dit levert een duidelijker resultaat op vanuit het perspectief van andere .NET-talen, omdat het tuple-formulier overeenkomt met de manier waarop argumenten worden doorgegeven in .NET-methoden.
De curriede vorm wordt meestal gebruikt met functies die zijn gemaakt met behulp van let
bindingen.
In de volgende pseudocode ziet u voorbeelden van tuple- en curriede argumenten.
// Tuple form.
member this.SomeMethod(param1, param2) = ...
// Curried form.
let function1 param1 param2 = ...
Gecombineerde vormen zijn mogelijk wanneer sommige argumenten zich in tuples bevinden en sommige niet.
let function2 param1 (param2a, param2b) param3 = ...
Andere patronen kunnen ook worden gebruikt in parameterlijsten, maar als het parameterpatroon niet overeenkomt met alle mogelijke invoer, is er mogelijk een onvolledige overeenkomst tijdens de runtime. De uitzondering MatchFailureException
wordt gegenereerd wanneer de waarde van een argument niet overeenkomt met de patronen die zijn opgegeven in de parameterlijst. De compiler geeft een waarschuwing wanneer een parameterpatroon onvolledige overeenkomsten toestaat. Ten minste één ander patroon is meestal handig voor parameterlijsten en dat is het jokertekenpatroon. U gebruikt het jokertekenpatroon in een parameterlijst wanneer u alleen argumenten wilt negeren die worden opgegeven. De volgende code illustreert het gebruik van het jokertekenpatroon in een lijst met argumenten.
let makeList _ = [ for i in 1 .. 100 -> i * i ]
// The arguments 100 and 200 are ignored.
let list1 = makeList 100
let list2 = makeList 200
Het jokertekenpatroon kan handig zijn wanneer u de argumenten die worden doorgegeven niet nodig hebt, zoals in het hoofdinvoerpunt van een programma, wanneer u niet geïnteresseerd bent in de opdrachtregelargumenten die normaal gesproken worden opgegeven als een tekenreeksmatrix, zoals in de volgende code.
[<EntryPoint>]
let main _ =
printfn "Entry point!"
0
Andere patronen die soms in argumenten worden gebruikt, zijn het as
patroon en id-patronen die zijn gekoppeld aan gediscrimineerde samenvoegingen en actieve patronen. U kunt het gediscrimineerde union-patroon in één geval als volgt gebruiken.
type Slice = Slice of int * int * string
let GetSubstring1 (Slice(p0, p1, text)) =
printfn "Data begins at %d and ends at %d in string %s" p0 p1 text
text[p0..p1]
let substring = GetSubstring1 (Slice(0, 4, "Et tu, Brute?"))
printfn "Substring: %s" substring
De uitvoer is als volgt.
Data begins at 0 and ends at 4 in string Et tu, Brute?
Et tu
Actieve patronen kunnen handig zijn als parameters, bijvoorbeeld bij het transformeren van een argument in een gewenste indeling, zoals in het volgende voorbeeld:
type Point = { x : float; y : float }
let (| Polar |) { x = x; y = y} =
( sqrt (x*x + y*y), System.Math.Atan (y/ x) )
let radius (Polar(r, _)) = r
let angle (Polar(_, theta)) = theta
U kunt het as
patroon gebruiken om een overeenkomende waarde op te slaan als een lokale waarde, zoals wordt weergegeven in de volgende coderegel.
let GetSubstring2 (Slice(p0, p1, text) as s) = s
Een ander patroon dat af en toe wordt gebruikt, is een functie die het laatste argument ongenaamd laat door, als hoofdtekst van de functie, een lambda-expressie op te geven die onmiddellijk een patroonovereenkomst uitvoert op het impliciete argument. Een voorbeeld hiervan is de volgende coderegel.
let isNil = function [] -> true | _::_ -> false
Deze code definieert een functie die een algemene lijst gebruikt en retourneert true
als de lijst leeg is en false
anders. Het gebruik van dergelijke technieken kan code moeilijker leesbaar maken.
Soms zijn patronen met onvolledige overeenkomsten handig, bijvoorbeeld als u weet dat de lijsten in uw programma slechts drie elementen bevatten, kunt u een patroon zoals het volgende gebruiken in een parameterlijst.
let sum [a; b; c;] = a + b + c
Het gebruik van patronen met onvolledige overeenkomsten is het beste gereserveerd voor snelle prototypen en andere tijdelijke toepassingen. De compiler geeft een waarschuwing voor dergelijke code uit. Dergelijke patronen kunnen het algemene geval van alle mogelijke invoer niet dekken en zijn daarom niet geschikt voor onderdeel-API's.
Benoemde argumenten
Argumenten voor methoden kunnen worden opgegeven op positie in een lijst met door komma's gescheiden argumenten of ze kunnen expliciet worden doorgegeven aan een methode door de naam op te geven, gevolgd door een gelijkteken en de waarde die moet worden doorgegeven. Indien opgegeven door de naam op te geven, kunnen ze in een andere volgorde worden weergegeven dan die in de declaratie wordt gebruikt.
Benoemde argumenten kunnen code beter leesbaar en beter aanpasbaar maken aan bepaalde typen wijzigingen in de API, zoals het opnieuw ordenen van methodeparameters.
Benoemde argumenten zijn alleen toegestaan voor methoden, niet voor let
-gebonden functies, functiewaarden of lambda-expressies.
In het volgende codevoorbeeld ziet u het gebruik van benoemde argumenten.
type SpeedingTicket() =
member this.GetMPHOver(speed: int, limit: int) = speed - limit
let CalculateFine (ticket : SpeedingTicket) =
let delta = ticket.GetMPHOver(limit = 55, speed = 70)
if delta < 20 then 50.0 else 100.0
let ticket1 : SpeedingTicket = SpeedingTicket()
printfn "%f" (CalculateFine ticket1)
In een aanroep naar een klasseconstructor kunt u de waarden van eigenschappen van de klasse instellen met behulp van een syntaxis die vergelijkbaar is met die van benoemde argumenten. In het volgende voorbeeld ziet u deze syntaxis.
type Account() =
let mutable balance = 0.0
let mutable number = 0
let mutable firstName = ""
let mutable lastName = ""
member this.AccountNumber
with get() = number
and set(value) = number <- value
member this.FirstName
with get() = firstName
and set(value) = firstName <- value
member this.LastName
with get() = lastName
and set(value) = lastName <- value
member this.Balance
with get() = balance
and set(value) = balance <- value
member this.Deposit(amount: float) = this.Balance <- this.Balance + amount
member this.Withdraw(amount: float) = this.Balance <- this.Balance - amount
let account1 = new Account(AccountNumber=8782108,
FirstName="Darren", LastName="Parker",
Balance=1543.33)
Zie Constructors (F#) voor meer informatie.
Dezelfde techniek, bedoeld om eigenschapssetters aan te roepen, is ook van toepassing op elke methode die objecten retourneert (zoals fabrieksmethoden):
type Widget() =
member val Width = 1 with get,set
member val Height = 1 with get,set
type WidgetFactory =
static member MakeNewWidget() =
new Widget()
static member AdjustWidget(w: Widget) =
w
let w = WidgetFactory.MakeNewWidget(Width=10)
w.Width // = 10
w.Height // = 1
WidgetFactory.AdjustWidget(w, Height=10)
w.Height // = 10
Houd er rekening mee dat deze leden willekeurige werkzaamheden kunnen uitvoeren. De syntaxis is in feite een korte hand om eigenschapssetters aan te roepen voordat de uiteindelijke waarde wordt geretourneerd.
Optionele parameters
U kunt een optionele parameter voor een methode opgeven met behulp van een vraagteken vóór de parameternaam. Vanuit het perspectief van de gebruiker worden optionele parameters geïnterpreteerd als het F#-optietype, zodat u query's kunt uitvoeren op de normale manier waarop de optietypen worden opgevraagd, met behulp van een match
expressie met Some
en None
. Optionele parameters zijn alleen toegestaan voor leden, niet voor functies die zijn gemaakt met behulp van let
bindingen.
U kunt bestaande optionele waarden doorgeven aan de methode op parameternaam, zoals ?arg=None
of ?arg=arg
?arg=Some(3)
. Dit kan handig zijn bij het bouwen van een methode die optionele argumenten doorgeeft aan een andere methode.
U kunt ook een functie defaultArg
gebruiken, waarmee een standaardwaarde van een optioneel argument wordt ingesteld. De defaultArg
functie gebruikt de optionele parameter als het eerste argument en de standaardwaarde als de tweede.
In het volgende voorbeeld ziet u het gebruik van optionele parameters.
type DuplexType =
| Full
| Half
type Connection(?rate0 : int, ?duplex0 : DuplexType, ?parity0 : bool) =
let duplex = defaultArg duplex0 Full
let parity = defaultArg parity0 false
let mutable rate = match rate0 with
| Some rate1 -> rate1
| None -> match duplex with
| Full -> 9600
| Half -> 4800
do printfn "Baud Rate: %d Duplex: %A Parity: %b" rate duplex parity
let conn1 = Connection(duplex0 = Full)
let conn2 = Connection(duplex0 = Half)
let conn3 = Connection(300, Half, true)
let conn4 = Connection(?duplex0 = None)
let conn5 = Connection(?duplex0 = Some(Full))
let optionalDuplexValue : option<DuplexType> = Some(Half)
let conn6 = Connection(?duplex0 = optionalDuplexValue)
De uitvoer is als volgt.
Baud Rate: 9600 Duplex: Full Parity: false
Baud Rate: 4800 Duplex: Half Parity: false
Baud Rate: 300 Duplex: Half Parity: true
Baud Rate: 9600 Duplex: Full Parity: false
Baud Rate: 9600 Duplex: Full Parity: false
Baud Rate: 4800 Duplex: Half Parity: false
Voor de interop tussen C# en Visual Basic kunt u de kenmerken [<Optional; DefaultParameterValue<(...)>]
in F# gebruiken, zodat bellers een argument zien als optioneel. Dit is gelijk aan het definiëren van het argument als optioneel in C# zoals in MyMethod(int i = 3)
.
open System
open System.Runtime.InteropServices
type C =
static member Foo([<Optional; DefaultParameterValue("Hello world")>] message) =
printfn $"{message}"
U kunt ook een nieuw object opgeven als een standaardparameterwaarde. Het lid kan bijvoorbeeld Foo
een optionele CancellationToken
invoer hebben als invoer:
open System.Threading
open System.Runtime.InteropServices
type C =
static member Foo([<Optional; DefaultParameterValue(CancellationToken())>] ct: CancellationToken) =
printfn $"{ct}"
De waarde die als argument DefaultParameterValue
wordt opgegeven, moet overeenkomen met het type parameter. Het volgende is bijvoorbeeld niet toegestaan:
type C =
static member Wrong([<Optional; DefaultParameterValue("string")>] i:int) = ()
In dit geval genereert de compiler een waarschuwing en worden beide kenmerken helemaal genegeerd. Houd er rekening mee dat de standaardwaarde null
moet worden getypt, omdat anders het verkeerde type wordt afgeleid door de compiler, dat wil [<Optional; DefaultParameterValue(null:obj)>] o:obj
zeggen.
Doorgeven per verwijzing
Het doorgeven van een F#-waarde per verwijzing omvat byrefs, die beheerde aanwijzertypen zijn. Richtlijnen voor het type dat u wilt gebruiken:
- Gebruik
inref<'T>
deze optie als u de aanwijzer alleen hoeft te lezen. - Gebruik
outref<'T>
deze optie als u alleen naar de aanwijzer hoeft te schrijven. - Gebruik
byref<'T>
deze optie als u zowel moet lezen van als schrijven naar de aanwijzer.
let example1 (x: inref<int>) = printfn $"It's %d{x}"
let example2 (x: outref<int>) = x <- x + 1
let example3 (x: byref<int>) =
printfn $"It's %d{x}"
x <- x + 1
let test () =
// No need to make it mutable, since it's read-only
let x = 1
example1 &x
// Needs to be mutable, since we write to it
let mutable y = 2
example2 &y
example3 &y // Now 'y' is 3
Omdat de parameter een aanwijzer is en de waarde onveranderbaar is, blijven wijzigingen in de waarde behouden na de uitvoering van de functie.
U kunt een tuple als retourwaarde gebruiken om parameters out
op te slaan in .NET-bibliotheekmethoden. U kunt de out
parameter ook behandelen als een byref
parameter. In het volgende codevoorbeeld ziet u beide manieren.
// TryParse has a second parameter that is an out parameter
// of type System.DateTime.
let (b, dt) = System.DateTime.TryParse("12-20-04 12:21:00")
printfn "%b %A" b dt
// The same call, using an address of operator.
let mutable dt2 = System.DateTime.Now
let b2 = System.DateTime.TryParse("12-20-04 12:21:00", &dt2)
printfn "%b %A" b2 dt2
Parametermatrices
Af en toe is het noodzakelijk om een functie te definiëren die een willekeurig aantal parameters van het heterogene type gebruikt. Het zou niet praktisch zijn om alle mogelijke overbelaste methoden te maken om rekening te houden met alle typen die kunnen worden gebruikt. De .NET-implementaties bieden ondersteuning voor dergelijke methoden via de parametermatrixfunctie. Een methode die een parametermatrix in de handtekening gebruikt, kan worden geleverd met een willekeurig aantal parameters. De parameters worden in een matrix geplaatst. Het type van de matrixelementen bepaalt de parametertypen die aan de functie kunnen worden doorgegeven. Als u de parametermatrix System.Object
definieert als het elementtype, kan clientcode waarden van elk type doorgeven.
In F# kunnen parametermatrices alleen worden gedefinieerd in methoden. Ze kunnen niet worden gebruikt in zelfstandige functies of functies die zijn gedefinieerd in modules.
U definieert een parametermatrix met behulp van het ParamArray
kenmerk. Het ParamArray
kenmerk kan alleen worden toegepast op de laatste parameter.
De volgende code illustreert zowel het aanroepen van een .NET-methode die een parametermatrix gebruikt als de definitie van een type in F# met een methode die een parametermatrix gebruikt.
open System
type X() =
member this.F([<ParamArray>] args: Object[]) =
for arg in args do
printfn "%A" arg
[<EntryPoint>]
let main _ =
// call a .NET method that takes a parameter array, passing values of various types
Console.WriteLine("a {0} {1} {2} {3} {4}", 1, 10.0, "Hello world", 1u, true)
let xobj = new X()
// call an F# method that takes a parameter array, passing values of various types
xobj.F("a", 1, 10.0, "Hello world", 1u, true)
0
Wanneer u in een project wordt uitgevoerd, is de uitvoer van de vorige code als volgt:
a 1 10 Hello world 1 True
"a"
1
10.0
"Hello world"
1u
true