Přetypování a převody (F#)
Tento článek popisuje podporu převodů typů v jazyce F#.
Aritmetické typy
Jazyk F# poskytuje operátory převodu pro aritmetické převody mezi různými primitivními typy, například mezi celočíselnými a plovoucími čísly. Integrální operátory a operátory převodu znaků mají zaškrtnuté a nezaškrtnuté formuláře; operátory s plovoucí desetinou čárkou a operátor převodu enum
ne. Nezaškrtnuté formuláře jsou definovány v FSharp.Core.Operators
a zaškrtnuté formuláře jsou definovány v FSharp.Core.Operators.Checked
. Kontrolované formuláře kontrolují přetečení a generují výjimku za běhu, pokud výsledná hodnota překročí limity cílového typu.
Každý z těchto operátorů má stejný název jako název cílového typu. Například v následujícím kódu, ve kterém jsou typy explicitně opatřeny poznámkami, byte
se zobrazí se dvěma různými významy. První výskyt je typ a druhý je operátor převodu.
let x : int = 5
let b : byte = byte x
Následující tabulka ukazuje operátory převodu definované v jazyce F#.
Operátor | Popis |
---|---|
byte |
Převede na bajt, 8bitový typ bez znaménka. |
sbyte |
Převést na podepsaný bajt. |
int16 |
Převeďte na 16bitové celé číslo se signedm. |
uint16 |
Převede na 16bitové celé číslo bez znaménka. |
int32, int |
Převeďte na 32bitové celé číslo se signedm. |
uint32 |
Převede na 32bitové celé číslo bez znaménka. |
int64 |
Převeďte na 64bitové celé číslo se signedem. |
uint64 |
Převede na 64bitové celé číslo bez znaménka. |
nativeint |
Převede na nativní celé číslo. |
unativeint |
Převede na celé celé číslo bez znaménka. |
float, double |
Převeďte na 64bitové číslo s dvojitou přesností IEEE s plovoucí desetinnou čárkou. |
float32, single |
Převede na 32bitové číslo s jednou přesností na IEEE s plovoucí desetinnou čárkou. |
decimal |
Převést na System.Decimal . |
char |
Převést na System.Char znak Unicode. |
enum |
Převeďte na výčtový typ. |
Kromě předdefinovaných primitivních typů můžete tyto operátory použít s typy, které implementují op_Explicit
nebo op_Implicit
metody s příslušnými podpisy. Operátor převodu int
například funguje s libovolným typem, který poskytuje statickou metodu op_Explicit
, která přebírá typ jako parametr a vrací int
. Zvláštní výjimka obecného pravidla, že metody nelze přetížit návratovým typem, můžete to provést pro op_Explicit
a op_Implicit
.
Výčtové typy
Operátor enum
je obecný operátor, který přebírá jeden parametr typu, který představuje typ převodu enum
na. Když se převede na výčtový typ, pokusí se odvozování typu určit typ enum
, na který chcete převést. V následujícím příkladu není proměnná col1
explicitně opatřena poznámkami, ale její typ je odvozen z pozdějšího testu rovnosti. Kompilátor proto může odvodit, že převádíte na Color
výčet. Alternativně můžete zadat poznámku k typu jako col2
v následujícím příkladu.
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
Cílový typ výčtu můžete také explicitně zadat jako parametr typu, jako v následujícím kódu:
let col3 = enum<Color> 3
Všimněte si, že výčty fungují pouze v případě, že základní typ výčtu je kompatibilní s převedeným typem. V následujícím kódu se převod nepodaří zkompilovat kvůli neshodě mezi int32
a uint32
.
// Error: types are incompatible
let col4 : Color = enum 2u
Další informace naleznete v tématu Výčty.
Přetypování typů objektů
Převod mezi typy v hierarchii objektů je zásadní pro objektově orientované programování. Existují dva základní typy převodů: přetypování nahoru (přetypování) a přetypování dolů (downcasting). Přetypování hierarchie znamená přetypování z odvozeného odkazu na odkaz na základní objekt. Takové přetypování je zaručeno, že funguje, pokud je základní třída v hierarchii dědičnosti odvozené třídy. Přetypování hierarchie ze základního odkazu na odvozený objekt je úspěšné pouze v případě, že je objekt skutečně instancí správného cílového (odvozeného) typu nebo typu odvozeného z cílového typu.
Jazyk F# poskytuje operátory pro tyto typy převodů. Operátor :>
přetypuje hierarchii a :?>
operátor přetypuje hierarchii dolů.
Přesměrovávání
V mnoha objektově orientovaných jazycích je přesměrování implicitní; v jazyce F# se pravidla mírně liší. Upcasting se použije automaticky, když předáte argumenty metodám typu objektu. Pro funkce vázané na let v modulu však není přetypování automatické, pokud typ parametru není deklarován jako flexibilní typ. Další informace naleznete v tématu Flexibilní typy.
Operátor :>
provádí statické přetypování, což znamená, že úspěch přetypování je určen v době kompilace. Pokud se přetypování, které používá :>
kompilace úspěšně, jedná se o platné přetypování a nemá šanci na selhání v době běhu.
K provedení takového převodu upcast
můžete také použít operátor. Následující výraz určuje převod hierarchie:
upcast expression
Při použití operátoru upcast se kompilátor pokusí odvodit typ, na který převádíte z kontextu. Pokud kompilátor nemůže určit cílový typ, kompilátor hlásí chybu. Může být vyžadována poznámka typu.
Downcasting
Operátor :?>
provádí dynamické přetypování, což znamená, že úspěch přetypování je určen za běhu. Přetypování, které používá :?>
operátor, se nekontroluje v době kompilace, ale při spuštění se provede pokus přetypovat na zadaný typ. Pokud je objekt kompatibilní s cílovým typem, přetypování proběhne úspěšně. Pokud objekt není kompatibilní s cílovým typem, modul runtime vyvolá výjimku InvalidCastException
.
Operátor můžete také použít downcast
k provedení dynamického převodu typu. Následující výraz určuje převod hierarchie na typ odvozený z kontextu programu:
downcast expression
upcast
Pokud kompilátor nemůže odvodit konkrétní cílový typ z kontextu, oznámí chybu. Může být vyžadována poznámka typu.
Následující kód znázorňuje použití :>
operátorů a :?>
operátorů. Kód ukazuje, že :?>
operátor je nejlepší použít, když víte, že převod bude úspěšný, protože vyvolá InvalidCastException
, pokud převod selže. Pokud nevíte, že převod bude úspěšný, je vhodnější test typu, který používá match
výraz, protože zabraňuje režii při generování výjimky.
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
Vzhledem k tomu, že obecné operátory downcast
a upcast
spoléhají na odvození typu k určení argumentu a návratového typu, můžete nahradit let base1 = d1 :> Base1
v předchozím příkladu kódu .let base1: Base1 = upcast d1
Je vyžadována poznámka typu, protože upcast
sama o sobě nemohla určit základní třídu.
Implicitní převody upcastu
Implicitní upcasty se vkládají v následujících situacích:
Při poskytování parametru pro funkci nebo metodu se známým pojmenovaným typem. To zahrnuje, když se volání metody stane konstruktorem, jako jsou výpočetní výrazy nebo řezy.
Při přiřazování nebo ztlumení pole záznamu nebo vlastnosti, která má známý pojmenovaný typ.
Pokud má větev
if/then/else
nebomatch
výraz známý cílový typ vyplývající z jiné větve nebo celkového známého typu.Pokud má prvek seznamu, pole nebo sekvenčního výrazu známý cílový typ.
Představte si například následující kód:
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")
Tady větve podmíněného výpočetního objektu a TextReader
StreamReader
v uvedeném pořadí. Ve druhé větvi je TextReader
známý cílový typ z poznámky k typu metody a z první větve. To znamená, že ve druhé větvi není potřeba žádné přesměrové vysílání.
Pokud chcete zobrazit upozornění v každém okamžiku, použije se další implicitní upcast, můžete povolit upozornění 3388 (/warnon:3388
nebo vlastnost <WarnOn>3388</WarnOn>
).
Implicitní číselné převody
Jazyk F# používá ve většině případů explicitní rozšíření číselných typů prostřednictvím operátorů převodu. Například explicitní rozšíření je nutné pro většinu číselných typů, jako int8
int16
je nebo nebo od float32
do float64
, nebo v případě, že je neznámý typ zdroje nebo cíle.
Implicitní rozšíření je však povoleno pro 32bitové celá čísla rozšířená na 64bitové celá čísla ve stejných situacích jako implicitní upcasty. Představte si například typický tvar rozhraní API:
type Tensor(…) =
static member Create(sizes: seq<int64>) = Tensor(…)
Lze použít celočíselné literály pro int64:
Tensor.Create([100L; 10L; 10L])
Nebo celočíselné literály pro int32:
Tensor.Create([int64 100; int64 10; int64 10])
Rozšíření probíhá automaticky, aby bylo při int32
odvozování typu známo, že nativeint
int64
double
int32
zdrojový i cílový typ. int32
V případech, jako jsou předchozí příklady, int32
se tedy dají použít literály:
Tensor.Create([100; 10; 10])
Volitelně můžete také povolit upozornění 3389 (/warnon:3389
nebo vlastnost <WarnOn>3389</WarnOn>
) k zobrazení upozornění v každém bodě implicitního číselného rozšíření.
. Implicitní převody ve stylu NET
Rozhraní .NET API umožňují definici statických op_Implicit
metod poskytovat implicitní převody mezi typy. Ty se použijí automaticky v kódu jazyka F# při předávání argumentů metodám. Představte si například následující kód, který provádí explicitní volání op_Implicit
metod:
open System.Xml.Linq
let purchaseOrder = XElement.Load("PurchaseOrder.xml")
let partNos = purchaseOrder.Descendants(XName.op_Implicit "Item")
. Převody stylu op_Implicit
NET se automaticky použijí pro výrazy argumentů, pokud jsou typy dostupné pro zdrojový výraz a cílový typ:
open System.Xml.Linq
let purchaseOrder = XElement.Load("PurchaseOrder.xml")
let partNos = purchaseOrder.Descendants("Item")
Můžete také povolit upozornění 3395 (/warnon:3395
nebo vlastnost <WarnOn>3395</WarnOn>
) zobrazit upozornění v každém bodě a . Používá se implicitní převod ve stylu NET.
. Převody ve stylu op_Implicit
NET se také použijí automaticky pro výrazy argumentů bez metody ve stejných situacích jako implicitní upcasty. Pokud se ale používá široce nebo nevhodně, implicitní převody můžou špatně pracovat s odvozováním typu a vést k tomu, že kód, který je obtížně srozumitelnější. Z tohoto důvodu vždy generují upozornění, pokud se používají v ne argumentových pozicích.
Chcete-li zobrazit upozornění v každém okamžiku, kdy je . Implicitní převod ve stylu NET se používá pro argument bez metody, můžete povolit upozornění 3391 (/warnon:3391
nebo vlastnost <WarnOn>3391</WarnOn>
).
Souhrn upozornění souvisejících s převody
Pro použití implicitních převodů jsou k dispozici následující volitelná upozornění:
/warnon:3388
(další implicitní upcast)/warnon:3389
(implicitní numerické rozšíření)/warnon:3391
(op_Implicit
při argumentech, které nejsou metodami, ve výchozím nastavení)/warnon:3395
(op_Implicit
v argumentech metody)