Gjutning och konverteringar (F#)
I den här artikeln beskrivs stöd för typkonverteringar i F#.
Aritmetiska typer
F# tillhandahåller konverteringsoperatorer för aritmetiska konverteringar mellan olika primitiva typer, till exempel mellan heltals- och flyttalstyper. Operatorerna integral- och teckenkonvertering har kontrollerat och avmarkerat formulär. flyttalsoperatorerna och konverteringsoperatorn enum
inte. De omarkerade formulären definieras i FSharp.Core.Operators
och de markerade formulären definieras i FSharp.Core.Operators.Checked
. De markerade formulären söker efter spill och genererar ett körningsundatag om det resulterande värdet överskrider måltypens gränser.
Var och en av dessa operatorer har samma namn som namnet på måltypen. I följande kod, där typerna uttryckligen kommenteras, byte
visas till exempel med två olika betydelser. Den första förekomsten är typen och den andra är konverteringsoperatorn.
let x : int = 5
let b : byte = byte x
I följande tabell visas konverteringsoperatorer som definierats i F#.
Operatör | beskrivning |
---|---|
byte |
Konvertera till byte, en 8-bitars osignerad typ. |
sbyte |
Konvertera till signerad byte. |
int16 |
Konvertera till ett 16-bitars signerat heltal. |
uint16 |
Konvertera till ett 16-bitars osignerat heltal. |
int32, int |
Konvertera till ett 32-bitars signerat heltal. |
uint32 |
Konvertera till ett 32-bitars osignerat heltal. |
int64 |
Konvertera till ett 64-bitars signerat heltal. |
uint64 |
Konvertera till ett 64-bitars osignerat heltal. |
nativeint |
Konvertera till ett inbyggt heltal. |
unativeint |
Konvertera till ett osignerat inbyggt heltal. |
float, double |
Konvertera till ett 64-bitars IEEE-flyttalsnummer med dubbel precision. |
float32, single |
Konvertera till ett 32-bitars IEEE-flyttalsnummer med enkel precision. |
decimal |
Konvertera till System.Decimal . |
char |
Konvertera till System.Char , ett Unicode-tecken. |
enum |
Konvertera till en uppräknad typ. |
Förutom inbyggda primitiva typer kan du använda dessa operatorer med typer som implementerar op_Explicit
eller op_Implicit
metoder med lämpliga signaturer. Konverteringsoperatorn int
fungerar till exempel med alla typer som tillhandahåller en statisk metod op_Explicit
som tar typen som en parameter och returnerar int
. Som ett särskilt undantag till den allmänna regeln att metoder inte kan överbelastas av returtyp kan du göra detta för op_Explicit
och op_Implicit
.
Uppräknade typer
Operatorn enum
är en allmän operator som tar en typparameter som representerar typen av som enum
ska konverteras till. När den konverteras till en uppräknad typ försöker typinferensen fastställa vilken typ av enum
som du vill konvertera till. I följande exempel kommenteras inte variabeln col1
uttryckligen, men dess typ härleds från det senare likhetstestet. Kompilatorn kan därför härleda att du konverterar till en Color
uppräkning. Du kan också ange en typanteckning, som i col2
följande exempel.
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
Du kan också ange måluppräkningstypen explicit som en typparameter, som i följande kod:
let col3 = enum<Color> 3
Observera att uppräkningsuppsättningarna endast fungerar om den underliggande typen av uppräkning är kompatibel med den typ som konverteras. I följande kod kan konverteringen inte kompileras på grund av matchningsfelet mellan int32
och uint32
.
// Error: types are incompatible
let col4 : Color = enum 2u
Mer information finns i Uppräkningar.
Typ av gjutningsobjekt
Konvertering mellan typer i en objekthierarki är grundläggande för objektorienterad programmering. Det finns två grundläggande typer av konverteringar: gjutning (uppcasting) och nedgjutning (nedkastning). Om du gjuter en hierarki innebär det att du gjuter från en referens för härledda objekt till en basobjektreferens. En sådan gjutning fungerar garanterat så länge basklassen finns i arvshierarkin för den härledda klassen. Gjutning av en hierarki, från en basobjektreferens till en referens för härledda objekt, lyckas bara om objektet faktiskt är en instans av rätt måltyp (härledd) eller en typ som härletts från måltypen.
F# tillhandahåller operatorer för dessa typer av konverteringar. Operatorn :>
genererar hierarkin och operatorn :?>
kastar ned hierarkin.
Uppcasting
På många objektorienterade språk är uppsändning implicit. I F# skiljer sig reglerna något. Uppsändning tillämpas automatiskt när du skickar argument till metoder för en objekttyp. För let-bound-funktioner i en modul sker dock inte uppsändning automatiskt, såvida inte parametertypen deklareras som en flexibel typ. Mer information finns i Flexibla typer.
Operatorn :>
utför en statisk gjutning, vilket innebär att gjutningens framgång bestäms vid kompileringstillfället. Om en cast som använder :>
kompilering är korrekt är det en giltig gjutning och har ingen risk för fel vid körning.
Du kan också använda operatorn upcast
för att utföra en sådan konvertering. Följande uttryck anger en konvertering uppåt i hierarkin:
upcast expression
När du använder den upparbetade operatorn försöker kompilatorn härleda den typ som du konverterar till från kontexten. Om kompilatorn inte kan fastställa måltypen rapporterar kompilatorn ett fel. En typanteckning kan krävas.
Nedständande
Operatorn :?>
utför en dynamisk gjutning, vilket innebär att gjutningens framgång bestäms vid körning. En gjutning som använder operatorn :?>
kontrolleras inte vid kompileringstillfället, men vid körning görs ett försök att casta till den angivna typen. Om objektet är kompatibelt med måltypen lyckas gjutningen. Om objektet inte är kompatibelt med måltypen genererar körningen en InvalidCastException
.
Du kan också använda operatorn downcast
för att utföra en dynamisk typkonvertering. Följande uttryck anger en konvertering nedåt i hierarkin till en typ som härleds från programkontext:
downcast expression
När det gäller operatorn upcast
rapporterar kompilatorn ett fel om kompilatorn inte kan härleda en specifik måltyp från kontexten. En typanteckning kan krävas.
Följande kod illustrerar användningen av operatorerna :>
och :?>
. Koden visar att operatorn används bäst när du vet att konverteringen :?>
lyckas, eftersom den utlöser InvalidCastException
om konverteringen misslyckas. Om du inte vet att en konvertering lyckas är ett typtest som använder ett match
uttryck bättre eftersom det undviker kostnaden för att generera ett undantag.
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
Eftersom de allmänna operatorerna downcast
och upcast
förlitar sig på typinferens för att fastställa argumentet och returtypen kan du ersätta let base1 = d1 :> Base1
i föregående kodexempel med let base1: Base1 = upcast d1
.
En typanteckning krävs, eftersom upcast
det i sig inte gick att fastställa basklassen.
Implicita uppcast-konverteringar
Implicita uppsändningar infogas i följande situationer:
När du tillhandahåller en parameter till en funktion eller metod med en känd namngiven typ. Detta inkluderar när en konstruktion, till exempel beräkningsuttryck eller segmentering, blir ett metodanrop.
När du tilldelar eller muterar ett postfält eller en egenskap som har en känd namngiven typ.
När en gren av en
if/then/else
ellermatch
ett uttryck har en känd måltyp som härrör från en annan gren eller övergripande känd typ.När ett element i en lista, matris eller sekvensuttryck har en känd måltyp.
Tänk till exempel på följande kod:
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")
Här grenarna av den villkorsstyrda beräkningen en TextReader
respektive StreamReader
. På den andra grenen är TextReader
den kända måltypen från typanteckningen på metoden och från den första grenen. Det innebär att ingen uppsändning behövs på den andra grenen.
Du kan aktivera varning 3388 (/warnon:3388
eller egenskap <WarnOn>3388</WarnOn>
) om du vill visa en varning vid varje tidpunkt som ytterligare implicit upcast används.
Implicita numeriska konverteringar
F# använder explicit breddning av numeriska typer i de flesta fall via konverteringsoperatorer. Till exempel krävs explicit breddning för de flesta numeriska typer, till exempel int8
eller int16
, eller från float32
till float64
eller när antingen käll- eller måltypen är okänd.
Implicit breddning tillåts dock för 32-bitars heltal vidgade till 64-bitars heltal, i samma situationer som implicita uppsändningar. Tänk dig till exempel en typisk API-form:
type Tensor(…) =
static member Create(sizes: seq<int64>) = Tensor(…)
Heltalsliteraler för int64 kan användas:
Tensor.Create([100L; 10L; 10L])
Eller heltalsliteraler för int32:
Tensor.Create([int64 100; int64 10; int64 10])
Breddning sker automatiskt för int32
till int64
, int32
till nativeint
och int32
till double
, när både käll- och måltyp är kända under typinferens. Så i fall som tidigare exempel int32
kan literaler användas:
Tensor.Create([100; 10; 10])
Du kan också aktivera varning 3389 (/warnon:3389
eller egenskapen <WarnOn>3389</WarnOn>
) för att visa en varning vid varje tidpunkt som implicit numerisk breddning används.
. Implicita konverteringar i NET-format
MED .NET-API:er kan definitionen av op_Implicit
statiska metoder tillhandahålla implicita konverteringar mellan typer. Dessa tillämpas automatiskt i F#-kod när argument skickas till metoder. Tänk dig till exempel att följande kod gör explicita anrop till op_Implicit
metoder:
open System.Xml.Linq
let purchaseOrder = XElement.Load("PurchaseOrder.xml")
let partNos = purchaseOrder.Descendants(XName.op_Implicit "Item")
. NET-stilkonverteringar op_Implicit
tillämpas automatiskt för argumentuttryck när typer är tillgängliga för källuttryck och måltyp:
open System.Xml.Linq
let purchaseOrder = XElement.Load("PurchaseOrder.xml")
let partNos = purchaseOrder.Descendants("Item")
Du kan också aktivera varning 3395 (/warnon:3395
eller egenskapen <WarnOn>3395</WarnOn>
) för att visa en varning vid varje punkt en . Implicit konvertering i NET-format används.
. NET-konverteringar op_Implicit
tillämpas också automatiskt för icke-metodargumentuttryck i samma situationer som implicita uppsändningar. Men när de används ofta eller olämpligt kan implicita konverteringar interagera dåligt med typinferens och leda till kod som är svårare att förstå. Därför genererar dessa alltid varningar när de används i icke-argumentpositioner.
Om du vill visa en varning vid varje punkt som en . Implicit konvertering i NET-format används för ett argument som inte är en metod. Du kan aktivera varning 3391 (/warnon:3391
eller egenskap <WarnOn>3391</WarnOn>
).
Sammanfattning av varningar relaterade till konverteringar
Följande valfria varningar tillhandahålls för användning av implicita konverteringar:
/warnon:3388
(ytterligare implicit uppsändning)/warnon:3389
(implicit numerisk breddning)/warnon:3391
(op_Implicit
vid argument som inte är metod, på som standard)/warnon:3395
(op_Implicit
vid metodargument)