Konversi eksplisit dan konversi (F#)

Artikel ini menjelaskan dukungan untuk konversi jenis di F#.

Jenis aritmatika

F# menyediakan operator konversi untuk konversi aritmatika antara berbagai jenis primitif, seperti antara jenis bilangan bulat dan titik mengambang. Operator konversi integral dan karakter telah memberi centang dan menghapus centang formulir; operator titik mengambang dan operator konversi enum tidak melakukannya. Formulir yang tidak dicentang didefinisikan dalam FSharp.Core.Operators dan formulir yang dicentang didefinisikan dalam FSharp.Core.Operators.Checked. Formulir yang dicentang memeriksa luapan dan menghasilkan pengecualian runtime bahasa umum jika nilai yang dihasilkan melebihi batas jenis target.

Setiap operator ini memiliki nama yang sama dengan nama jenis tujuan. Misalnya, dalam kode berikut, saat jenisnya secara eksplisit dianotasikan, byte muncul dengan dua arti yang berbeda. Kemunculan pertama adalah jenis dan yang kedua merupakan operator konversi.

let x : int = 5

let b : byte = byte x

Tabel berikut menunjukkan operator konversi yang didefinisikan dalam F#.

Operator Deskripsi
byte Mengonversikan ke byte, jenis 8-bit tanpa tanda tangan.
sbyte Mengonversikan ke byte yang ditandatangani.
int16 Mengonversikan ke bilangan bulat bertanda tangan 16-bit.
uint16 Mengonversikan ke bilangan bulat tanpa tanda tangan 16-bit.
int32, int Mengonversikan ke bilangan bulat yang ditandatangani 32-bit.
uint32 Mengonversikan ke bilangan bulat tanpa tanda tangan 32-bit.
int64 Mengonversikan ke bilangan bulat yang ditandatangani 64-bit.
uint64 Mengonversikan ke bilangan bulat tanpa tanda tangan 64-bit.
nativeint Mengonversikan ke bilangan bulat asli.
unativeint Mengonversikan ke bilangan bulat asli tanpa tanda tangan.
float, double Mengonversikan ke angka titik mengambang IEEE presisi ganda 64-bit.
float32, single Mengonversikan ke angka titik mengambang IEEE presisi tunggal 32-bit.
decimal Mengonversikan ke System.Decimal.
char Mengonversikan ke System.Char, karakter Unicode.
enum Mengonversikan ke jenis enumerasi.

Selain jenis primitif bawaan, Anda dapat menggunakan berbagai operator ini dengan jenis yang menerapkan metode op_Explicit atau op_Implicit dengan tanda tangan yang sesuai. Misalnya, operator konversi int bekerja dengan jenis apa pun yang menyediakan metode statis op_Explicit yang mengambil jenis sebagai parameter dan mengembalikan int. Sebagai pengecualian khusus untuk aturan umum, metode tersebut tidak dapat kelebihan beban berdasarkan jenis pengembalian, Anda dapat melakukan ini untuk op_Explicit dan op_Implicit.

Jenis Enumerasi

Operator enum merupakan operator generik yang mengambil satu parameter jenis yang mewakili jenis enum yang akan dikonversikan. Saat dikonversi ke jenis enumerasi, jenis inferensi mencoba menentukan jenis enum yang ingin Anda konversi. Dalam contoh berikut, variabel col1 tidak dianotasikan secara eksplisit, tetapi jenisnya disimpulkan dari pengujian kesetaraan yang lebih baru. Oleh karena itu, kompilator dapat menyimpulkan bahwa Anda mengonversinya menjadi enumerasi Color. Atau, Anda dapat menyediakan anotasi jenis, seperti dalam col2 contoh berikut.

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

Anda juga dapat menentukan jenis enumerasi target secara eksplisit sebagai parameter jenis, seperti dalam kode berikut:

let col3 = enum<Color> 3

Perhatikan bahwa konversi eksplisit enumerasi hanya berfungsi jika jenis enumerasi yang mendasar kompatibel dengan jenis yang dikonversi. Dalam kode berikut, konversi gagal dikompilasi karena ketidakcocokan antara int32 dan uint32.

// Error: types are incompatible
let col4 : Color = enum 2u

Untuk informasi selengkapnya, lihat Enumerasi.

Jenis Objek Konversi Eksplisit

Konversi antara jenis dalam hierarki objek sangat penting untuk pemrograman berorientasi objek. Ada dua jenis konversi dasar: konversi eksplisit ke atas (upcasting) dan konversi eksplisit ke bawah (downcasting). Konversi eksplisit ke atas hierarki berarti melakukan konversi eksplisit dari referensi objek turunan ke referensi objek dasar. Konversi eksplisit semacam itu dijamin berfungsi selama kelas dasar berada dalam hierarki warisan dari kelas turunan. Konversi eksplisit ke bawah hierarki, dari referensi objek dasar ke referensi objek turunan, hanya berhasil jika objek benar-benar adalah instans dari jenis tujuan yang benar (diturunkan) atau jenis yang berasal dari jenis tujuan.

F# menyediakan berbagai operator untuk jenis konversi ini. Operator :> melakukan konversi eksplisit ke atas hierarki, dan operator :?> melakukan konversi eksplisit ke bawah hierarki.

Upcasting

Dalam berbagai bahasa pemrograman berorientasi objek, upcasting bersifat implisit; di F#, aturannya sedikit berbeda. Upcasting diterapkan secara otomatis saat Anda meneruskan argumen ke metode pada jenis objek. Namun, untuk fungsi let-bound dalam modul, upcasting tidak otomatis, kecuali jenis parameter dinyatakan sebagai jenis fleksibel. Untuk informasi selengkapnya, lihat Jenis Fleksibel.

Operator :> melakukan konversi eksplisit statis, yang berarti bahwa keberhasilan konversi eksplisit ditentukan pada waktu kompilasi. Jika konversi eksplisit yang menggunakan :> berhasil melakukan kompilasi, itu adalah konversi eksplisit yang valid dan tidak memiliki kemungkinan kegagalan pada durasi.

Anda juga dapat menggunakan operator upcast untuk melakukan konversi tersebut. Ekspresi berikut menentukan konversi ke hierarki:

upcast expression

Saat Anda menggunakan operator upcast, kompilator mencoba menyimpulkan jenis yang Anda konversikan dari konteks. Jika kompilator tidak dapat menentukan jenis target, kompilator melaporkan kesalahan. Jenis anotasi mungkin diperlukan.

Downcasting

Operator :?> melakukan konversi eksplisit dinamis, yang berarti bahwa keberhasilan konversi eksplisit ditentukan pada waktu durasi. Konversi eksplisit yang menggunakan operator :?> tidak diperiksa pada waktu kompilasi; tetapi pada waktu durasi, upaya dilakukan untuk melakukan konversi eksplisit ke jenis yang ditentukan. Jika objek kompatibel dengan jenis target, konversi eksplisit berhasil. Jika objek tidak kompatibel dengan jenis target, durasi akan menaikkan InvalidCastException.

Anda juga dapat menggunakan operator downcast untuk melakukan konversi jenis dinamis. Ekspresi berikut menentukan konversi ke bawah hierarki ke jenis yang disimpulkan dari konteks program:

downcast expression

Adapun operator upcast, jika kompilator tidak dapat menyimpulkan jenis target tertentu dari konteks, operator melaporkan kesalahan. Jenis anotasi mungkin diperlukan.

Kode berikut mengilustrasikan penggunaan operator :> dan :?>. Kode ini menggambarkan bahwa operator :?> paling baik digunakan ketika Anda meyakini bahwa konversi akan berhasil, karena kode ini melemparkan InvalidCastException jika konversi gagal. Jika Anda tidak yakin apakah konversi akan berhasil, pengujian jenis yang menggunakan ekspresi match lebih baik karena menghindari overhead menghasilkan pengecualian.

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

Karena operator downcast dan upcast generik mengandalkan inferensi jenis untuk menentukan jenis argumen dan pengembalian, Anda dapat mengganti let base1 = d1 :> Base1 dalam contoh kode sebelumnya dengan let base1: Base1 = upcast d1.

Anotasi jenis diperlukan, karena upcast tidak dapat menentukan kelas dasar dengan sendirinya.

Konversi upcast implisit

Upcast implisit disisipkan dalam situasi berikut:

  • Saat memberikan parameter ke fungsi atau metode dengan jenis bernama yang diketahui. Ini termasuk ketika konstruksi seperti ekspresi komputasi atau pemotongan menjadi panggilan metode.

  • Saat menetapkan ke atau bermutasi bidang rekaman atau properti yang memiliki jenis bernama yang diketahui.

  • Ketika cabang ekspresi if/then/else atau match memiliki jenis target yang diketahui timbul dari cabang lain atau jenis yang diketahui secara keseluruhan.

  • Saat elemen ekspresi daftar, array, atau urutan memiliki jenis target yang diketahui.

Sebagai contoh, perhatikan kode berikut:

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")

Di sini cabang dari komputasi kondisional untuk TextReader dan StreamReader. Pada cabang kedua, jenis target yang diketahui merupakan TextReader yang berasal dari jenis anotasi dalam metode, dan dari cabang pertama. Ini berarti tidak ada upcast yang diperlukan pada cabang kedua.

Untuk menampilkan peringatan di setiap titik, upcast implisit tambahan digunakan, Anda dapat mengaktifkan peringatan 3388 (/warnon:3388 atau properti <WarnOn>3388</WarnOn>).

Konversi numerik implisit

F# menggunakan pelebaran eksplisit jenis numerik dalam banyak kasus melalui operator konversi. Misalnya, pelebaran eksplisit diperlukan untuk sebagian besar jenis numerik, seperti int8 atau int16, atau dari float32 ke float64, atau jika jenis sumber atau tujuan tidak diketahui.

Namun, pelebaran implisit diizinkan untuk bilangan bulat 32-bit yang dilebarkan menjadi bilangan bulat 64-bit, dalam situasi yang sama dengan upcast implisit. Misalnya, pertimbangkan bentuk API yang khas:

type Tensor(…) =
    static member Create(sizes: seq<int64>) = Tensor(…)

Harfiah bilangan bulat untuk int64 dapat digunakan:

Tensor.Create([100L; 10L; 10L])

Atau harfiah bilangan bulat untuk int32:

Tensor.Create([int64 100; int64 10; int64 10])

Pelebaran terjadi secara otomatis untuk int32 ke int64, int32 ke nativeint, dan int32 ke double, ketika baik jenis sumber dan tujuan diketahui selama inferensi jenis. Jadi dalam kasus seperti contoh sebelumnya, harfiah int32 dapat digunakan:

Tensor.Create([100; 10; 10])

Anda juga dapat secara opsional mengaktifkan peringatan 3389 (/warnon:3389 atau properti <WarnOn>3389</WarnOn>) untuk menunjukkan peringatan di setiap titik pelebaran numerik implisit yang digunakan.

Konversi implisit gaya .NET

API .NET memungkinkan definisi metode statis op_Implicit untuk memberikan konversi implisit antar jenis. Ini diterapkan secara otomatis dalam kode F# saat meneruskan argumen ke metode. Misalnya, pertimbangkan kode berikut yang melakukan panggilan eksplisit ke metode op_Implicit:

open System.Xml.Linq

let purchaseOrder = XElement.Load("PurchaseOrder.xml")
let partNos = purchaseOrder.Descendants(XName.op_Implicit "Item")

Konversi op_Implicit gaya .NET diterapkan secara otomatis untuk ekspresi argumen saat jenis tersedia untuk ekspresi sumber dan jenis target:

open System.Xml.Linq

let purchaseOrder = XElement.Load("PurchaseOrder.xml")
let partNos = purchaseOrder.Descendants("Item")

Anda juga dapat secara opsional mengaktifkan peringatan 3395 (/warnon:3395 atau properti <WarnOn>3395</WarnOn>) untuk menunjukkan peringatan di setiap titik pelebaran numerik implisit gaya .NET yang digunakan.

Konversi op_Implicit gaya .NET juga diterapkan secara otomatis untuk ekspresi argumen non-metode dalam situasi yang sama dengan upcast implisit. Namun, saat digunakan secara lebar atau tidak tepat, konversi implisit dapat berinteraksi dengan buruk terhadap inferensi jenis dan mengarah ke kode yang lebih sulit untuk dipahami. Untuk alasan ini, konversi ini selalu menghasilkan peringatan ketika digunakan dalam posisi non-argumen.

Untuk menampilkan peringatan di setiap titik bahwasanya konversi implisit gaya .NET sedang digunakan untuk argumen non-metode, Anda dapat mengaktifkan peringatan 3391 (/warnon:3391 atau properti <WarnOn>3391</WarnOn>).

Peringatan opsional berikut disediakan untuk penggunaan konversi implisit:

  • /warnon:3388 (upcast implisit tambahan)
  • /warnon:3389 (pelebaran numerik implisit)
  • /warnon:3391 (op_Implicit pada argumen non-metode, aktif secara default)
  • /warnon:3395 (op_Implicit pada argumen metode)

Lihat juga