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
ataumatch
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>
).
Ringkasan peringatan yang terkait dengan konversi
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)