Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Rekaman mewakili agregat sederhana dari nilai bernama, secara opsional dengan anggota. Keduanya dapat berupa struct atau jenis referensi. Mereka adalah jenis referensi secara default.
Sintaksis
[ attributes ]
type [accessibility-modifier] typename =
[accessibility-modifier] {
[ mutable ] label1 : type1;
[ mutable ] label2 : type2;
...
}
[ member-list ]
Sebelum accessibility modifiertypename mempengaruhi visibilitas seluruh jenis, dan secara public default. Yang kedua accessibility modifier hanya memengaruhi konstruktor dan bidang.
Komentar
Dalam sintaks sebelumnya, typename adalah nama jenis rekaman, label1 dan label2 adalah nama nilai, disebut sebagai label, dan type1 dan type2 adalah jenis nilai ini.
daftar anggota adalah daftar anggota opsional untuk jenis tersebut. Anda dapat menggunakan [<Struct>] atribut untuk membuat rekaman struct daripada rekaman yang merupakan jenis referensi.
Berikut ini adalah beberapa contoh.
// Labels are separated by semicolons when defined on the same line.
type Point = { X: float; Y: float; Z: float }
// You can define labels on their own line with or without a semicolon.
type Customer =
{ First: string
Last: string
SSN: uint32
AccountNumber: uint32 }
// A struct record.
[<Struct>]
type StructPoint = { X: float; Y: float; Z: float }
Saat setiap label berada di baris terpisah, titik koma bersifat opsional.
Anda bisa mengatur nilai dalam ekspresi yang dikenal sebagai ekspresi rekaman. Pengkompilasi menyimpulkan jenis dari label yang digunakan (jika label cukup berbeda dari jenis rekaman lainnya). Kurung kurawal ({ }) mengapit ekspresi rekaman. Kode berikut menunjukkan ekspresi rekaman yang menginisialisasi rekaman dengan tiga elemen float dengan label x, y dan z.
let mypoint = { X = 1.0; Y = 1.0; Z = -1.0 }
Jangan gunakan formulir yang dipersingkat jika mungkin ada jenis lain yang juga memiliki label yang sama.
type Point = { X: float; Y: float; Z: float }
type Point3D = { X: float; Y: float; Z: float }
// Ambiguity: Point or Point3D?
let mypoint3D = { X = 1.0; Y = 1.0; Z = 0.0 }
Label jenis yang terakhir dideklarasikan lebih diutamakan daripada jenis yang dideklarasikan sebelumnya, jadi dalam contoh sebelumnya, mypoint3D disimpulkan menjadi Point3D. Anda dapat secara eksplisit menentukan jenis catatan, seperti dalam kode berikut.
let myPoint1 = { Point.X = 1.0; Y = 1.0; Z = 0.0 }
Metode dapat didefinisikan untuk jenis rekaman sama seperti untuk jenis kelas.
Membuat Rekaman dengan Menggunakan Ekspresi Rekaman
Anda bisa menginisialisasi rekaman dengan menggunakan label yang ditentukan dalam rekaman. Ekspresi yang melakukan ini disebut sebagai ekspresi rekaman. Gunakan kurung kurawal untuk mengapit ekspresi rekaman dan menggunakan titik koma sebagai pemisah.
Contoh berikut menunjukkan cara membuat rekaman.
type MyRecord = { X: int; Y: int; Z: int }
let myRecord1 = { X = 1; Y = 2; Z = 3 }
Titik koma setelah bidang terakhir dalam ekspresi rekaman dan dalam definisi jenis bersifat opsional, terlepas dari apakah semua bidang berada dalam satu baris.
Saat membuat rekaman, Anda harus menyediakan nilai untuk setiap bidang. Anda tidak dapat merujuk ke nilai bidang lain dalam ekspresi inisialisasi untuk bidang apa pun.
Dalam kode berikut, jenis myRecord2 disimpulkan dari nama bidang. Secara opsional, Anda dapat menentukan nama jenis secara eksplisit.
let myRecord2 =
{ MyRecord.X = 1
MyRecord.Y = 2
MyRecord.Z = 3 }
Bentuk konstruksi rekaman lain dapat berguna ketika Anda harus menyalin rekaman yang ada, dan mungkin mengubah beberapa nilai bidang. Baris kode berikut mengilustrasikan ini.
let myRecord3 = { myRecord2 with Y = 100; Z = 2 }
Bentuk ekspresi rekaman ini disebut ekspresi rekaman salin dan perbarui.
Rekaman tidak dapat diubah secara default; namun, Anda dapat dengan mudah membuat rekaman yang dimodifikasi dengan menggunakan ekspresi salin dan perbarui. Anda juga dapat secara eksplisit menentukan bidang yang dapat diubah.
type Car =
{ Make: string
Model: string
mutable Odometer: int }
let myCar =
{ Make = "Fabrikam"
Model = "Coupe"
Odometer = 108112 }
myCar.Odometer <- myCar.Odometer + 21
Jangan gunakan atribut DefaultValue dengan bidang rekaman. Pendekatan yang lebih baik adalah menentukan instans rekaman default dengan bidang yang diinisialisasi ke nilai default lalu menggunakan ekspresi rekaman salin dan perbarui untuk mengatur bidang apa pun yang berbeda dari nilai default.
// Rather than use [<DefaultValue>], define a default record.
type MyRecord =
{ Field1 : int
Field2 : int }
let defaultRecord1 = { Field1 = 0; Field2 = 0 }
let defaultRecord2 = { Field1 = 1; Field2 = 25 }
// Use the with keyword to populate only a few chosen fields
// and leave the rest with default values.
let rr3 = { defaultRecord1 with Field2 = 42 }
Membuat Rekaman Yang Saling Rekursif
Kadang-kadang saat membuat rekaman, Anda mungkin ingin membuatnya bergantung pada jenis lain yang ingin Anda tentukan setelahnya. Ini adalah kesalahan kompilasi kecuali Anda menentukan jenis rekaman agar saling rekursif.
Menentukan rekaman yang saling rekursif dilakukan dengan and kata kunci. Ini memungkinkan Anda menautkan 2 jenis rekaman atau lebih bersama-sama.
Misalnya, kode berikut mendefinisikan Person dan Address mengetik sebagai rekursif bersama:
// Create a Person type and use the Address type that is not defined
type Person =
{ Name: string
Age: int
Address: Address }
// Define the Address type which is used in the Person record
and Address =
{ Line1: string
Line2: string
PostCode: string
Occupant: Person }
Untuk membuat instans keduanya, Anda melakukan hal berikut:
// Create a Person type and use the Address type that is not defined
let rec person =
{
Name = "Person name"
Age = 12
Address =
{
Line1 = "line 1"
Line2 = "line 2"
PostCode = "abc123"
Occupant = person
}
}
Jika Anda menentukan contoh sebelumnya tanpa and kata kunci, maka itu tidak akan dikompilasi. Kata and kunci diperlukan untuk definisi yang saling rekursif.
Pencocokan Pola dengan Rekaman
Rekaman dapat digunakan dengan pencocokan pola. Anda dapat menentukan beberapa bidang secara eksplisit dan menyediakan variabel untuk bidang lain yang akan ditetapkan saat kecocokan terjadi. Contoh kode berikut mengilustrasikan hal ini.
type Point3D = { X: float; Y: float; Z: float }
let evaluatePoint (point: Point3D) =
match point with
| { X = 0.0; Y = 0.0; Z = 0.0 } -> printfn "Point is at the origin."
| { X = xVal; Y = 0.0; Z = 0.0 } -> printfn "Point is on the x-axis. Value is %f." xVal
| { X = 0.0; Y = yVal; Z = 0.0 } -> printfn "Point is on the y-axis. Value is %f." yVal
| { X = 0.0; Y = 0.0; Z = zVal } -> printfn "Point is on the z-axis. Value is %f." zVal
| { X = xVal; Y = yVal; Z = zVal } -> printfn "Point is at (%f, %f, %f)." xVal yVal zVal
evaluatePoint { X = 0.0; Y = 0.0; Z = 0.0 }
evaluatePoint { X = 100.0; Y = 0.0; Z = 0.0 }
evaluatePoint { X = 10.0; Y = 0.0; Z = -1.0 }
Output kode ini adalah sebagai berikut.
Point is at the origin.
Point is on the x-axis. Value is 100.000000.
Point is at (10.000000, 0.000000, -1.000000).
Rekaman dan anggota
Anda dapat menentukan anggota pada rekaman seperti yang Anda bisa dengan kelas. Tidak ada dukungan untuk bidang. Pendekatan umum adalah mendefinisikan Default anggota statis untuk konstruksi catatan yang mudah:
type Person =
{ Name: string
Age: int
Address: string }
static member Default =
{ Name = "Phillip"
Age = 12
Address = "123 happy fun street" }
let defaultPerson = Person.Default
Jika Anda menggunakan pengidentifikasi mandiri, pengidentifikasi tersebut mengacu pada instans rekaman yang anggotanya dipanggil:
type Person =
{ Name: string
Age: int
Address: string }
member this.WeirdToString() =
this.Name + this.Address + string this.Age
let p = { Name = "a"; Age = 12; Address = "abc123" }
let weirdString = p.WeirdToString()
Pengubah Aksesibilitas pada Rekaman
Kode berikut mengilustrasikan penggunaan pengubah aksesibilitas. Ada tiga file dalam proyek: Module1.fs, , Test1.fsdan Test2.fs. Jenis catatan internal dan jenis rekaman dengan konstruktor privat ditentukan dalam Module1.
// Module1.fs
module Module1
type internal internalRecd = { X: int }
type recdWithInternalCtor = private { Y: int }
Test1.fs Dalam file, rekaman internal harus diinisialisasi dengan internal pengubah akses, itu karena tingkat perlindungan nilai dan rekaman harus cocok, dan keduanya harus termasuk dalam rakitan yang sama.
// Test1.fs
module Test1
open Module1
let myInternalRecd1 = { X = 2 } // This line will cause a compiler error.
let internal myInternalRecd2 = { X = 4 } // This is OK
Test2.fs Dalam file, rekaman dengan konstruktor privat tidak dapat diinisialisasi secara langsung karena tingkat perlindungan konstruktor.
// Test2.fs
module Test2
open Module1
let myRecdWithInternalCtor = { Y = 6 } // This line will cause a compiler error.
Untuk informasi selengkapnya tentang pengubah aksesibilitas, lihat artikel Kontrol Akses .
Perbedaan Antara Rekaman dan Kelas
Bidang rekaman berbeda dari bidang kelas karena bidang tersebut secara otomatis diekspos sebagai properti, dan digunakan dalam pembuatan dan penyalinan rekaman. Konstruksi rekam juga berbeda dari konstruksi kelas. Dalam tipe catatan, Anda tidak dapat menentukan konstruktor. Sebaliknya, sintaks konstruksi yang dijelaskan dalam topik ini berlaku. Kelas tidak memiliki hubungan langsung antara parameter, bidang, dan properti konstruktor.
Seperti jenis serikat dan struktur, rekaman memiliki semantik kesetaraan struktural. Kelas memiliki semantik kesetaraan referensi. Contoh kode berikut menunjukkan hal ini.
type RecordTest = { X: int; Y: int }
let record1 = { X = 1; Y = 2 }
let record2 = { X = 1; Y = 2 }
if (record1 = record2) then
printfn "The records are equal."
else
printfn "The records are unequal."
Output kode ini adalah sebagai berikut:
The records are equal.
Jika Anda menulis kode yang sama dengan kelas, dua objek kelas akan tidak sama karena kedua nilai akan mewakili dua objek pada tumpukan dan hanya alamat yang akan dibandingkan (kecuali jenis kelas mengambil alih System.Object.Equals metode).
Jika Anda memerlukan kesetaraan referensi untuk rekaman, tambahkan atribut [<ReferenceEquality>] di atas rekaman.