Dela via


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 eller match 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 float64eller 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 nativeintoch 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>).

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)

Se även