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.
Untuk berbagi bidang tanpa menggabungkan jenis model yang berbeda, gunakan spread rekaman, yang tersedia dimulai dengan F# 11.
Gunakan ...Type untuk menyumbang deklarasi bidang ke rekaman bernama baru, atau ...expression untuk menyumbang nilai bidang ke rekaman bernama atau anonim. Penggunaan kembali deklarasi dan pemetaan nilai adalah pilihan independen.
Menyusun jenis rekaman
Untuk berbagi bidang koneksi di seluruh jenis konfigurasi, tersebar Endpoint ke dalam deklarasi rekaman baru:
type Endpoint = { Host: string; Port: int }
type ClientOptions =
{ ...Endpoint
TimeoutSeconds: int }
ClientOptions memiliki Host, Port, dan TimeoutSeconds. Ini adalah jenis nominal yang berbeda, bukan Endpoint subjenis.
Beberapa sumber dan deklarasi eksplisit dapat muncul dalam definisi jenis yang sama:
type Logging = { LogLevel: string }
type LoggedClientOptions = { ...ClientOptions; ...Logging }
Hanya bidang rekaman yang berpartisipasi. Metode, properti yang bukan bidang rekaman, dan anggota statis tidak disalin. Sumber dapat berupa rekaman bernama atau jenis catatan anonim, termasuk singkatan untuk rekaman anonim. Tujuan dari sebaran jenis harus berupa jenis rekaman bernama.
Menyusun nilai rekaman
Salin titik akhir ke opsi klien, lalu gunakan catatan anonim untuk mengganti host:
let local = { Host = "localhost"; Port = 5432 }
let options: ClientOptions = { ...local; TimeoutSeconds = 30 }
let remote = {| ...options; Host = "db.example.com" |}
printn $"{remote.Host}:{remote.Port}, timeout {remote.TimeoutSeconds}s"
// db.example.com:5432, timeout 30s
Spread ekspresi menerima sumber dan tujuan rekaman bernama atau anonim, termasuk rekaman struct. Setiap bidang tujuan harus menerima nilai. Anotasi target eksplisit berguna ketika beberapa label berbagi jenis rekaman bernama.
Memperkuat jenis bidang
Deklarasi bidang yang lebih baru dapat menggantikan jenis bidang spread. Misalnya, mewakili pengidentifikasi yang diurai dengan type CustomerId = CustomerId of System.Guid, lalu membedakan rekaman yang diimpor dari satu dengan pengidentifikasi tersebut:
type ImportedCustomer = { Name: string; Id: string }
type IdentifiedCustomer = { ...ImportedCustomer; Id: CustomerId }
let identify (source: ImportedCustomer) =
match Guid.TryParse source.Id with
| true, id -> Ok ({ ...source; Id = CustomerId id }: IdentifiedCustomer)
| false, _ -> Error "Invalid customer identifier."
Guid.TryParse memvalidasi teks; sebaran hanya menyalin bidang.
CustomerId membedakan pengidentifikasi dari GUID lain tanpa memberlakukan aturan lebih lanjut.
Menggabungkan model independen
Buat deklarasi dari rekaman nominal yang dikumpulkan dan jenis catatan anonim, lalu rakit nilai dari sumber independen:
type PublicProductFields = { Name: string; UnitPrice: decimal }
type ProductRow = { ...PublicProductFields; InternalCost: decimal }
type StockCounts = {| InStock: int; Reserved: int |}
type ProductCard =
{ ...PublicProductFields
...StockCounts
CanOrder: bool }
let product: ProductRow =
{ Name = "Notebook"; UnitPrice = 12.50m; InternalCost = 4m }
let stock: StockCounts = {| InStock = 8; Reserved = 2 |}
let card: ProductCard =
{ ...product; ...stock; CanOrder = stock.InStock > stock.Reserved }
let viewState = {| ...card; Selected = false |}
let selected = {| ...viewState; Selected = true |}
Pasokan ProductCardsumber bernama dan anonim ; kartu kemudian memasok status tampilan anonim.
CanOrder dihitung sekali, tidak dihitung ulang ketika konstruksi nanti mengubah hitungan.
Project menjadi target yang diketahui
Jenis target memilih bidang yang akan disimpan. Beberapa sumber dapat menyediakan penggantian:
let endpoint: Endpoint = { ...options }
let overrides = {| Host = "db.example.com"; Port = 5433 |}
let production: ClientOptions =
{ ...options; ...overrides; TimeoutSeconds = 60 }
printn $"{endpoint.Host}:{endpoint.Port}"
printn $"{production.Host}:{production.Port}, timeout {production.TimeoutSeconds}s"
// localhost:5432
// db.example.com:5433, timeout 60s
endpoint hanya Host menyimpan dan Port; production menggabungkan sumber dan penimpaan eksplisit. Bidang eksplisit tidak ada dari target tetap kesalahan.
Rekaman anonim yang tidak diannotasi mengambil semua bidang yang dikontribusikan. Target bernama harus dapat disimpulkan. Sumber yang tidak diketahui memerlukan anotasi mereka sendiri, seperti (source: ClientOptions) pada parameter fungsi.
Urutan dan penimpaan bidang
Kontribusi dibaca dari kiri ke kanan. Untuk nama bidang berulang, kontribusi paling kanan menang. Aturan ini menerapkan keduanya ke deklarasi bidang dalam jenis dan ke nilai bidang dalam ekspresi.
let laterHost = {| Host = "db.example.com" |}
let corrected: Endpoint = { ...local; ...laterHost; Port = 5433 }
Setelah penimpaan jenis, berikan nilai jenis penggantian. Bidang eksplisit berulang, seperti { Host = "first"; Host = "second" }, tetap kesalahan.
Untuk deklarasi jenis, bidang yang menang muncul pada posisi kontribusi pemenangnya. Bidang dari sumber mempertahankan urutan sumbernya. Pesanan ini penting bagi konstruktor dan kesesuaian tanda tangan.
Evaluasi dan penyalinan
Ekspresi sumber spread berjalan sekali, dalam urutan sumber, bahkan jika kontribusi selanjutnya menggantikan setiap bidang yang mereka berikan:
let visits = ResizeArray<string>()
let hostFrom source host =
visits.Add source
{| Host = host |}
let selected =
{| ...hostFrom "first" "localhost"
...hostFrom "second" "db.example.com" |}
assert (selected.Host = "db.example.com")
assert (Seq.toList visits = [ "first"; "second" ])
Spread membuat rekaman baru dengan salinan bidang dangkal dan konversi yang sama dengan penugasan eksplisit. Objek yang dapat diubah yang dirujuk oleh bidang tetap dibagikan.
Pembaruan bidang berlapis berfungsi dengan spread, seperti yang dilakukan dengan salin dan perbarui:
let original = {| Connection = local; Retries = 3 |}
let changed = {| ...original; Connection.Port = 5433 |}
assert (changed.Connection.Port = 5433)
assert (original.Connection.Port = 5432)
Kontribusi selanjutnya untuk Connection menggantikan pembaruan berlapis sebelumnya ke bidang tersebut. Tempatkan penimpaan yang disederhanakan setelah penyebaran yang harus mereka ambil alih.
Peringatan tentang bidang ber bayangan
Penyebaran selanjutnya secara tidak sengaja dapat membatalkan pilihan eksplisit. Kompilator melaporkan peringatan ini secara default:
| Warning | Meaning |
|---|---|
| FS3897 | Bidang dari sebaran jenis membayangi deklarasi bidang eksplisit sebelumnya. |
| FS3898 | Bidang dari ekspresi menyebar membayangi pengikatan bidang eksplisit sebelumnya. |
Misalnya, { Host = "db.example.com"; ...local } mengambil Host dari local, bukan "db.example.com".
Untuk audit tumpang tindih lainnya, aktifkan peringatan keikutsertaan ini:
| Warning | Meaning |
|---|---|
| FS3905 | Jenis menyebar membayangi bidang dari penyebaran jenis sebelumnya. |
| FS3906 | Bidang eksplisit membayangi bidang dari penyebaran sebelumnya. |
| FS3907 | Ekspresi menyebar membayangi bidang dari sebaran ekspresi sebelumnya. |
<WarnOn>$(WarnOn);3905;3906;3907</WarnOn>
Pemberitahuan ini tidak mengubah perilaku paling kanan-menang.
Representasi dan kompatibilitas API
Jenis menyebarkan mutabilitas bidang salin dan atribut tingkat bidang. Mereka tidak menyalin atribut tingkat jenis; atribut tujuan sendiri [<Struct>] menentukan representasinya. Argumen jenis sumber harus memenuhi batasan generik sumber, termasuk batasan unit-of-measure.
Important
Gunakan spread jenis hanya ketika evolusi bersama disengaja. Perubahan bidang sumber dapat mengubah bidang publik, konstruktor, atribut, atau batasan tujuan tanpa pengeditan ke tujuan tersebut.
Spread tidak melewati pemeriksaan aksesibilitas. Rekaman nullable bukan sumber spread yang valid; menetapkan bahwa nilai nullable bukan null sebelum menggunakannya. Dependensi siklik melalui spread ditolak. Bidang rekaman rekursif biasa tidak dilarang hanya karena deklarasi juga menggunakan spread.
Jenis penyebaran berfungsi dalam .fsi file. Tanda tangan dapat menggunakan bidang eksplisit saat implementasi menggunakan spread, atau sebaliknya, asalkan bidang yang dihasilkan dan pesanannya setuju. Kutipan berisi konstruksi rekaman yang dijabarkan, bukan simpul spread.
Pilih spread atau copy-and-update
Gunakan ekspresi salin dan perbarui untuk pembaruan jenis yang sama dari satu sumber. Gunakan spread untuk menggabungkan sumber, proyek ke jenis rekaman yang berbeda, atau gunakan kembali deklarasi bidang. Jangan gabungkan with dan ... dalam ekspresi rekaman yang sama.
Spread tidak berlaku untuk koleksi, ekspresi objek, objek .NET arbitrer, atau rekaman C#. Mereka tidak memberikan sintaks pola rekaman. Konstruktor rekaman adalah fitur F# 11.2 terpisah.