Cast-conversies en conversies (F#)
In dit artikel wordt ondersteuning voor typeconversies in F# beschreven.
Rekenkundige typen
F# biedt conversieoperators voor rekenkundige conversies tussen verschillende primitieve typen, zoals tussen gehele getallen en drijvende kommatypen. De integrale en tekenconversieoperatoren hebben formulieren gecontroleerd en uitgeschakeld; de operatoren voor drijvende komma en de enum
conversieoperator niet. De niet-gecontroleerde formulieren worden gedefinieerd en FSharp.Core.Operators
de ingeschakelde formulieren worden gedefinieerd in FSharp.Core.Operators.Checked
. De ingeschakelde formulieren controleren op overloop en genereren een runtime-uitzondering als de resulterende waarde de limieten van het doeltype overschrijdt.
Elk van deze operators heeft dezelfde naam als de naam van het doeltype. In de volgende code, waarin de typen expliciet worden geannoteerd, byte
worden bijvoorbeeld weergegeven met twee verschillende betekenissen. Het eerste exemplaar is het type en de tweede is de conversieoperator.
let x : int = 5
let b : byte = byte x
In de volgende tabel ziet u conversieoperators die zijn gedefinieerd in F#.
Operator | Beschrijving |
---|---|
byte |
Converteren naar byte, een 8-bits niet-ondertekend type. |
sbyte |
Converteren naar ondertekende byte. |
int16 |
Converteren naar een 16-bits geheel getal dat is ondertekend. |
uint16 |
Converteren naar een 16-bits geheel getal zonder teken. |
int32, int |
Converteren naar een 32-bits geheel getal dat is ondertekend. |
uint32 |
Converteren naar een 32-bits geheel getal zonder teken. |
int64 |
Converteren naar een 64-bits geheel getal dat is ondertekend. |
uint64 |
Converteren naar een 64-bits geheel getal zonder teken. |
nativeint |
Converteren naar een systeemeigen geheel getal. |
unativeint |
Converteren naar een niet-ondertekend systeemeigen geheel getal. |
float, double |
Converteer naar een 64-bits IEEE-drijvendekommanummer met dubbele precisie. |
float32, single |
Converteer naar een 32-bits IEEE-drijvendekommagetal met één precisie. |
decimal |
Converteren naar System.Decimal . |
char |
Converteren naar System.Char , een Unicode-teken. |
enum |
Converteren naar een geïnventariseerd type. |
Naast ingebouwde primitieve typen kunt u deze operators gebruiken met typen die implementeren op_Explicit
of op_Implicit
methoden met de juiste handtekeningen. De int
conversieoperator werkt bijvoorbeeld met elk type dat een statische methode op_Explicit
biedt die het type als parameter gebruikt en retourneert int
. Als een speciale uitzondering op de algemene regel dat methoden niet kunnen worden overbelast door retourtype, kunt u dit doen voor op_Explicit
en op_Implicit
.
Geïnventariseerd type
De enum
operator is een algemene operator die één typeparameter gebruikt die het type vertegenwoordigt waarnaar enum
moet worden geconverteerd. Wanneer deze wordt geconverteerd naar een geïnventariseerd type, probeert deductie te bepalen naar welk type enum
u wilt converteren. In het volgende voorbeeld wordt de variabele col1
niet expliciet geannoteerd, maar het type wordt afgeleid van de latere gelijkheidstest. Daarom kan de compiler afleiden dat u converteert naar een Color
opsomming. U kunt ook een typeaantekening opgeven, zoals col2
in het volgende voorbeeld.
type Color =
| Red = 1
| Green = 2
| Blue = 3
// The target type of the conversion cannot be determined by type inference, so the type parameter must be explicit.
let col1 = enum<Color> 1
// The target type is supplied by a type annotation.
let col2 : Color = enum 2
U kunt ook het doelinventarisatietype expliciet opgeven als een typeparameter, zoals in de volgende code:
let col3 = enum<Color> 3
Houd er rekening mee dat de opsommingscasts alleen werken als het onderliggende type van de opsomming compatibel is met het type dat wordt geconverteerd. In de volgende code kan de conversie niet worden gecompileerd vanwege de verschillen tussen int32
en uint32
.
// Error: types are incompatible
let col4 : Color = enum 2u
Zie Opsommingen voor meer informatie.
Objecttypen casten
Conversie tussen typen in een objecthiërarchie is fundamenteel voor objectgeoriënteerde programmering. Er zijn twee basistypen conversies: cast-up (upcasting) en cast-down (downcasting). Het gieten van een hiërarchie betekent casten van een afgeleide objectverwijzing naar een basisobjectverwijzing. Een dergelijke cast werkt gegarandeerd zolang de basisklasse zich in de overnamehiërarchie van de afgeleide klasse bevindt. Het omlaag gieten van een hiërarchie, van een basisobjectverwijzing naar een afgeleide objectverwijzing, slaagt alleen als het object daadwerkelijk een exemplaar is van het juiste doeltype (afgeleid) of een type dat is afgeleid van het doeltype.
F# biedt operators voor deze typen conversies. De :>
operator gooit de hiërarchie omhoog en de :?>
operator cast de hiërarchie omlaag.
Upcasting
In veel objectgeoriënteerde talen is upcasting impliciet; in F# zijn de regels iets anders. Upcasting wordt automatisch toegepast wanneer u argumenten doorgeeft aan methoden voor een objecttype. Voor let-gebonden functies in een module is upcasting echter niet automatisch, tenzij het parametertype wordt gedeclareerd als een flexibel type. Zie Flexibele typen voor meer informatie.
De :>
operator voert een statische cast uit, wat betekent dat het succes van de cast tijdens het compileren wordt bepaald. Als een cast die compilaties gebruikt :>
, is het een geldige cast en heeft het geen kans op fouten tijdens runtime.
U kunt de upcast
operator ook gebruiken om een dergelijke conversie uit te voeren. Met de volgende expressie wordt een conversie van de hiërarchie opgegeven:
upcast expression
Wanneer u de upcast-operator gebruikt, probeert de compiler het type te afleiden waarnaar u converteert vanuit de context. Als de compiler het doeltype niet kan bepalen, meldt de compiler een fout. Mogelijk is een typeaantekening vereist.
Downcasting
De :?>
operator voert een dynamische cast uit, wat betekent dat het succes van de cast tijdens runtime wordt bepaald. Een cast die gebruikmaakt van de operator wordt niet gecontroleerd tijdens het :?>
compileren, maar tijdens runtime wordt geprobeerd om naar het opgegeven type te casten. Als het object compatibel is met het doeltype, slaagt de cast. Als het object niet compatibel is met het doeltype, genereert de runtime een InvalidCastException
.
U kunt de downcast
operator ook gebruiken om een dynamische typeconversie uit te voeren. Met de volgende expressie wordt een conversie naar beneden in de hiërarchie opgegeven naar een type dat wordt afgeleid van de programmacontext:
downcast expression
Wat de upcast
operator betreft, als de compiler een specifiek doeltype niet kan afleiden uit de context, wordt er een fout gerapporteerd. Mogelijk is een typeaantekening vereist.
De volgende code illustreert het gebruik van de :>
en :?>
operators. De code illustreert dat de operator het beste wordt gebruikt wanneer u weet dat de :?>
conversie slaagt, omdat deze wordt veroorzaakt InvalidCastException
als de conversie mislukt. Als u niet weet dat een conversie slaagt, is een typetest die gebruikmaakt van een match
expressie beter omdat hiermee de overhead van het genereren van een uitzondering wordt vermeden.
type Base1() =
abstract member F : unit -> unit
default u.F() =
printfn "F Base1"
type Derived1() =
inherit Base1()
override u.F() =
printfn "F Derived1"
let d1 : Derived1 = Derived1()
// Upcast to Base1.
let base1 = d1 :> Base1
// This might throw an exception, unless
// you are sure that base1 is really a Derived1 object, as
// is the case here.
let derived1 = base1 :?> Derived1
// If you cannot be sure that b1 is a Derived1 object,
// use a type test, as follows:
let downcastBase1 (b1 : Base1) =
match b1 with
| :? Derived1 as derived1 -> derived1.F()
| _ -> ()
downcastBase1 base1
Omdat de algemene operators downcast
en upcast
afhankelijk zijn van typedeductie om het argument en het retourtype te bepalen, kunt u in het vorige codevoorbeeld vervangen door let base1 = d1 :> Base1
let base1: Base1 = upcast d1
.
Een typeaantekening is vereist, omdat upcast
de basisklasse zelf niet kan worden bepaald.
Impliciete upcast-conversies
Impliciete upcasts worden ingevoegd in de volgende situaties:
Wanneer u een parameter opgeeft aan een functie of methode met een bekend benoemd type. Dit geldt ook wanneer een constructie zoals berekeningsexpressies of segmentering een methode-aanroep wordt.
Wanneer u een recordveld of eigenschap met een bekend benoemd type toewijst of dempt.
Wanneer een vertakking van een
if/then/else
ofmatch
expressie een bekend doeltype heeft dat voortvloeit uit een andere vertakking of algemeen bekend type.Wanneer een element van een lijst, matrix of reeksexpressie een bekend doeltype heeft.
Denk bijvoorbeeld aan de volgende code:
open System
open System.IO
let findInputSource () : TextReader =
if DateTime.Now.DayOfWeek = DayOfWeek.Monday then
// On Monday a TextReader
Console.In
else
// On other days a StreamReader
File.OpenText("path.txt")
Hier de vertakkingen van de voorwaardelijke berekening een TextReader
en StreamReader
respectievelijk. Op de tweede vertakking is TextReader
het bekende doeltype afkomstig van de typeaantekening voor de methode en van de eerste vertakking. Dit betekent dat er geen upcast nodig is voor de tweede vertakking.
Als u een waarschuwing wilt weergeven op elk punt dat een extra impliciete upcast wordt gebruikt, kunt u waarschuwing 3388 (/warnon:3388
of eigenschap <WarnOn>3388</WarnOn>
) inschakelen.
Impliciete numerieke conversies
F# maakt gebruik van expliciete verbreiding van numerieke typen in de meeste gevallen via conversieoperators. Expliciete widening is bijvoorbeeld nodig voor de meeste numerieke typen, zoals int8
of int16
, of van float32
naar float64
, of wanneer bron- of doeltype onbekend is.
Impliciete widening is echter toegestaan voor 32-bits gehele getallen die worden uitgebreid tot 64-bits gehele getallen, in dezelfde situaties als impliciete upcasts. Denk bijvoorbeeld aan een typische API-shape:
type Tensor(…) =
static member Create(sizes: seq<int64>) = Tensor(…)
Letterlijke waarden voor gehele getallen voor int64 kunnen worden gebruikt:
Tensor.Create([100L; 10L; 10L])
Of letterlijke gehele getallen voor int32:
Tensor.Create([int64 100; int64 10; int64 10])
Het breder maken gebeurt automatisch voorint32
, int32
tot nativeint
int64
en int32
metdouble
, wanneer zowel het bron- als het doeltype bekend zijn tijdens typedeductie. In gevallen zoals de vorige voorbeelden int32
kunnen letterlijke waarden dus worden gebruikt:
Tensor.Create([100; 10; 10])
U kunt de waarschuwing 3389 (/warnon:3389
of eigenschap <WarnOn>3389</WarnOn>
) desgewenst ook inschakelen om een waarschuwing weer te geven op elk punt dat impliciete numerieke widening wordt gebruikt.
. Impliciete conversies in NET-stijl
Met .NET-API's kan de definitie van op_Implicit
statische methoden impliciete conversies tussen typen bieden. Deze worden automatisch toegepast in F#-code bij het doorgeven van argumenten aan methoden. Denk bijvoorbeeld aan de volgende code die expliciete aanroepen naar methoden doet op_Implicit
:
open System.Xml.Linq
let purchaseOrder = XElement.Load("PurchaseOrder.xml")
let partNos = purchaseOrder.Descendants(XName.op_Implicit "Item")
. Conversies in NET-stijl op_Implicit
worden automatisch toegepast voor argumentexpressies wanneer typen beschikbaar zijn voor bronexpressie en doeltype:
open System.Xml.Linq
let purchaseOrder = XElement.Load("PurchaseOrder.xml")
let partNos = purchaseOrder.Descendants("Item")
U kunt de waarschuwing 3395 (/warnon:3395
of eigenschap <WarnOn>3395</WarnOn>
) desgewenst ook inschakelen om op elk punt een waarschuwing weer te geven. Impliciete conversie in NET-stijl wordt gebruikt.
. Net-stijlconversies op_Implicit
worden ook automatisch toegepast op expressies zonder methode-argument in dezelfde situaties als impliciete upcasts. Wanneer impliciete conversies echter veel of ongepast worden gebruikt, kunnen impliciete conversies slecht communiceren met typedeductie en leiden tot code die moeilijker te begrijpen is. Daarom genereren deze altijd waarschuwingen wanneer ze worden gebruikt in niet-argumentposities.
Een waarschuwing weergeven op elk punt dat een . Impliciete conversie in NET-stijl wordt gebruikt voor een argument dat niet de methode is, u kunt waarschuwing 3391 (/warnon:3391
of eigenschap <WarnOn>3391</WarnOn>
) inschakelen.
Samenvatting van waarschuwingen met betrekking tot conversies
De volgende optionele waarschuwingen worden geboden voor het gebruik van impliciete conversies:
/warnon:3388
(aanvullende impliciete upcast)/warnon:3389
(impliciete numerieke widening)/warnon:3391
(op_Implicit
bij niet-methodeargumenten, standaard ingeschakeld)/warnon:3395
(op_Implicit
bij methodeargumenten)