Sdílet prostřednictvím


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.Charznak 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 nebo match 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 TextReaderStreamReader 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 int8int16je 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 nativeintint64doubleint32 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>).

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)

Viz také