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.
F# memiliki dua area fitur utama yang berurusan dengan ruang pemrograman tingkat rendah:
- Jenis,
byref//inrefoutrefyang merupakan penunjuk terkelola. Mereka memiliki batasan penggunaan sehingga Anda tidak dapat mengkompilasi program yang tidak valid pada waktu proses. - Struct
byrefseperti, yang merupakan struktur yang memiliki semantik serupa dan pembatasan waktu kompilasi yang sama denganbyref<'T>. Salah satu contohnya adalah Span<T>.
Sintaksis
// Byref types as parameters
let f (x: byref<'T>) = ()
let g (x: inref<'T>) = ()
let h (x: outref<'T>) = ()
// Calling a function with a byref parameter
let mutable x = 3
f &x
// Declaring a byref-like struct
open System.Runtime.CompilerServices
[<Struct; IsByRefLike>]
type S(count1: int, count2: int) =
member x.Count1 = count1
member x.Count2 = count2
Byref, inref, dan outref
Ada tiga bentuk :byref
-
inref<'T>, pointer terkelola untuk membaca nilai yang mendasar. -
outref<'T>, pointer terkelola untuk menulis ke nilai yang mendasar. -
byref<'T>, pointer terkelola untuk membaca dan menulis nilai yang mendasar.
byref<'T> Dapat diteruskan di mana diharapkaninref<'T>. Demikian pula, dapat byref<'T> dilewati di mana diharapkan outref<'T> .
Menggunakan byrefs
Untuk menggunakan inref<'T>, Anda perlu mendapatkan nilai pointer dengan &:
open System
let f (dt: inref<DateTime>) =
printfn $"Now: %O{dt}"
let usage =
let dt = DateTime.Now
f &dt // Pass a pointer to 'dt'
Untuk menulis ke pointer dengan menggunakan outref<'T> atau byref<'T>, Anda juga harus membuat nilai yang Anda ambil pointer ke mutable.
open System
let f (dt: byref<DateTime>) =
printfn $"Now: %O{dt}"
dt <- DateTime.Now
// Make 'dt' mutable
let mutable dt = DateTime.Now
// Now you can pass the pointer to 'dt'
f &dt
Jika Anda hanya menulis pointer alih-alih membacanya, pertimbangkan untuk menggunakan outref<'T> alih-alih byref<'T>.
Semantik inref
Pertimbangkan kode berikut:
let f (x: inref<SomeStruct>) = x.SomeField
Secara semantik, ini berarti sebagai berikut:
- Pemegang
xpointer hanya dapat menggunakannya untuk membaca nilai . - Setiap penunjuk yang diperoleh ke
structbidang yang ditumpuk di dalamnyaSomeStructdiberi jenisinref<_>.
Berikut ini juga benar:
- Tidak ada implikasi bahwa utas atau alias lain tidak memiliki akses tulis ke
x. - Tidak ada implikasi yang
SomeStructtidak dapat diubah berdasarkan kebajikanxmenjadi .inref
Namun, untuk jenis nilai F# yang tidak dapat diubah, this pointer disimpulkan sebagai inref.
Semua aturan ini bersama-sama berarti bahwa pemegang inref pointer mungkin tidak memodifikasi konten langsung memori yang ditunjukkan.
Semantik outref
Tujuannya outref<'T> adalah untuk menunjukkan bahwa pointer hanya boleh ditulis. Secara tak terduga, outref<'T> mengizinkan membaca nilai yang mendasar meskipun namanya. Ini untuk tujuan kompatibilitas.
Secara semantik, outref<'T> tidak berbeda dari byref<'T>, kecuali untuk satu perbedaan: metode dengan outref<'T> parameter secara implisit dibangun menjadi jenis pengembalian tuple, seperti saat memanggil metode dengan [<Out>] parameter.
type C =
static member M1(x, y: _ outref) =
y <- x
true
match C.M1 1 with
| true, 1 -> printfn "Expected" // Fine with outref, error with byref
| _ -> printfn "Never matched"
Interop dengan C#
C# mendukung in ref kata kunci dan out ref , selain ref mengembalikan. Tabel berikut menunjukkan bagaimana F# menginterpretasikan apa yang dipancarkan C#:
| Konstruksi C# | Infersi F# |
|---|---|
ref mengembalikan nilai |
outref<'T> |
ref readonly mengembalikan nilai |
inref<'T> |
parameter in ref |
inref<'T> |
parameter out ref |
outref<'T> |
Tabel berikut ini memperlihatkan apa yang dipancarkan F#:
| Konstruksi F# | Konstruksi yang dipancarkan |
|---|---|
Argumen inref<'T> |
[In] atribut pada argumen |
inref<'T> kembali |
modreq atribut pada nilai |
inref<'T> dalam slot atau implementasi abstrak |
modreq pada argumen atau pengembalian |
Argumen outref<'T> |
[Out] atribut pada argumen |
Ketik aturan inferensi dan kelebihan beban
Jenis inref<'T> disimpulkan oleh pengkompilasi F# dalam kasus berikut:
- Parameter .NET atau jenis pengembalian yang memiliki
IsReadOnlyatribut. - Penunjuk
thispada jenis struct yang tidak memiliki bidang yang dapat diubah. - Alamat lokasi memori yang berasal dari pointer lain
inref<_>.
Ketika alamat implisit dari suatu inref sedang diambil, kelebihan beban dengan argumen jenis SomeType lebih disukai daripada kelebihan beban dengan argumen jenis inref<SomeType>. Contohnya:
type C() =
static member M(x: System.DateTime) = x.AddDays(1.0)
static member M(x: inref<System.DateTime>) = x.AddDays(2.0)
static member M2(x: System.DateTime, y: int) = x.AddDays(1.0)
static member M2(x: inref<System.DateTime>, y: int) = x.AddDays(2.0)
let res = System.DateTime.Now
let v = C.M(res)
let v2 = C.M2(res, 4)
Dalam kedua kasus, kelebihan beban yang diambil System.DateTime diselesaikan daripada kelebihan beban mengambil inref<System.DateTime>.
Struktur seperti byref
Selain byref//inrefoutref trio, Anda dapat menentukan struktur Anda sendiri yang dapat mematuhi byref-seperti semantik. Ini dilakukan dengan atribut IsByRefLikeAttribute:
open System
open System.Runtime.CompilerServices
[<IsByRefLike; Struct>]
type S(count1: Span<int>, count2: Span<int>) =
member x.Count1 = count1
member x.Count2 = count2
IsByRefLike tidak menyiratkan Struct. Keduanya harus ada pada jenis .
Struktur "byrefseperti" di F# adalah jenis nilai yang terikat tumpukan. Ini tidak pernah dialokasikan pada tumpukan terkelola. Struktur byrefseperti ini berguna untuk pemrograman berkinerja tinggi, karena diberlakukan dengan serangkaian pemeriksaan yang kuat tentang masa pakai dan non-pengambilan. Aturannya adalah:
- Mereka dapat digunakan sebagai parameter fungsi, parameter metode, variabel lokal, pengembalian metode.
- Mereka tidak boleh menjadi anggota statis atau instans kelas atau struktur normal.
- Mereka tidak dapat ditangkap oleh konstruksi penutupan apa pun (
asyncmetode atau ekspresi lambda). - Mereka tidak dapat digunakan sebagai parameter generik.
- Dimulai dengan F# 9, pembatasan ini dilonggarkan jika parameter generik didefinisikan dalam C# menggunakan anti-batasan struct memungkinkan ref. F# dapat membuat instans generik tersebut dalam jenis dan metode dengan jenis seperti byref. Sebagai beberapa contoh, ini memengaruhi jenis delegasi BCL (
Action<>,Func<>), antarmuka (IEnumerable<>,IComparable<>) dan argumen generik dengan fungsi akumulator yang disediakan pengguna (String.string Create<TState>(int length, TState state, SpanAction<char, TState> action)). - Tidak mungkin untuk menulis kode generik yang mendukung jenis seperti byref di F#.
- Dimulai dengan F# 9, pembatasan ini dilonggarkan jika parameter generik didefinisikan dalam C# menggunakan anti-batasan struct memungkinkan ref. F# dapat membuat instans generik tersebut dalam jenis dan metode dengan jenis seperti byref. Sebagai beberapa contoh, ini memengaruhi jenis delegasi BCL (
Poin terakhir ini sangat penting untuk pemrograman gaya alur F#, seperti |> halnya fungsi generik yang membuat parameter jenis inputnya. Pembatasan ini dapat dilonggarkan untuk |> di masa depan, karena sebaris dan tidak melakukan panggilan apa pun ke fungsi generik yang tidak bergaris dalam tubuhnya.
Meskipun aturan ini sangat membatasi penggunaan, mereka melakukannya untuk memenuhi janji komputasi berkinerja tinggi dengan cara yang aman.
Byref mengembalikan
Byref mengembalikan dari fungsi F# atau anggota dapat diproduksi dan dikonsumsi. Saat mengonsumsi byrefmetode -returning, nilai secara implisit didereferensikan. Contohnya:
let squareAndPrint (data : byref<int>) =
let squared = data*data // data is implicitly dereferenced
printfn $"%d{squared}"
Untuk mengembalikan byref nilai, variabel yang berisi nilai harus hidup lebih lama dari cakupan saat ini.
Selain itu, untuk mengembalikan byref, gunakan &value (di mana nilai adalah variabel yang hidup lebih lama dari cakupan saat ini).
let mutable sum = 0
let safeSum (bytes: Span<byte>) =
for i in 0 .. bytes.Length - 1 do
sum <- sum + int bytes[i]
&sum // sum lives longer than the scope of this function.
Untuk menghindari dereferensi implisit, seperti meneruskan referensi melalui beberapa panggilan berantai, gunakan &x (di mana x adalah nilainya).
Anda juga dapat langsung menetapkan ke pengembalian byref. Pertimbangkan program berikut (sangat penting):
type C() =
let mutable nums = [| 1; 3; 7; 15; 31; 63; 127; 255; 511; 1023 |]
override _.ToString() = String.Join(' ', nums)
member _.FindLargestSmallerThan(target: int) =
let mutable ctr = nums.Length - 1
while ctr > 0 && nums[ctr] >= target do ctr <- ctr - 1
if ctr > 0 then &nums[ctr] else &nums[0]
[<EntryPoint>]
let main argv =
let c = C()
printfn $"Original sequence: %O{c}"
let v = &c.FindLargestSmallerThan 16
v <- v*2 // Directly assign to the byref return
printfn $"New sequence: %O{c}"
0 // return an integer exit code
Ini adalah output:
Original sequence: 1 3 7 15 31 63 127 255 511 1023
New sequence: 1 3 7 30 31 63 127 255 511 1023
Cakupan untuk byref
Nilai let-bound tidak dapat memiliki referensi melebihi cakupan di mana nilai tersebut ditentukan. Misalnya, berikut ini tidak diizinkan:
let test2 () =
let x = 12
&x // Error: 'x' exceeds its defined scope!
let test () =
let x =
let y = 1
&y // Error: `y` exceeds its defined scope!
()
Ini mencegah Anda mendapatkan hasil yang berbeda tergantung pada apakah Anda mengkompilasi dengan pengoptimalan atau tidak.