ByRef’ler

F# alt düzey programlama alanıyla ilgilenen iki önemli özellik alanına sahiptir:

  • byref//inrefoutref Yönetilen işaretçiler olan türler. Çalışma zamanında geçersiz olan bir programı derleyemezsiniz diye kullanım kısıtlamaları vardır.
  • benzer byrefsemantiklere ve ile aynı derleme zamanı kısıtlamalarına byref<'T>sahip bir yapı olan bir -like yapısı. Örneklerden biri de şeklindedir Span<T>.

Sözdizimi

// 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 ve outref

üç biçimi byrefvardır:

  • inref<'T>, temel alınan değeri okumak için yönetilen bir işaretçi.
  • outref<'T>, temel alınan değere yazmak için yönetilen bir işaretçi.
  • byref<'T>, temel alınan değeri okumak ve yazmak için yönetilen bir işaretçi.

beklendiği byref<'T> yerde geçirilebilir inref<'T> . Benzer şekilde, bir byref<'T> beklendiği yerde outref<'T> geçirilebilir.

Byrefs'i kullanma

kullanmak inref<'T>için ile &bir işaretçi değeri almanız gerekir:

open System

let f (dt: inref<DateTime>) =
    printfn $"Now: %O{dt}"

let usage =
    let dt = DateTime.Now
    f &dt // Pass a pointer to 'dt'

veya byref<'T>kullanarak outref<'T> işaretçiye yazmak için, işaretçiyi yakalayabileceğiniz mutabledeğeri de yapmalısınız.

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

İşaretçiyi okumak yerine yalnızca yazıyorsanız yerine kullanmayı outref<'T>byref<'T>göz önünde bulundurun.

Inref semantiği

Aşağıdaki kodu inceleyin:

let f (x: inref<SomeStruct>) = x.SomeField

Bu, anlam olarak şu anlama gelir:

  • İşaretçinin x tutucusu yalnızca değeri okumak için kullanabilir.
  • İç SomeStruct içe yerleştirilmiş alanlara struct alınan tüm işaretçilere türü inref<_>verilir.

Aşağıdakiler de geçerlidir:

  • Diğer iş parçacıklarının veya diğer adların üzerinde xyazma erişimi olmamasının bir etkisi yoktur.
  • bir olma özelliğiyle x sabit olan bir inrefima SomeStruct yoktur.

Ancak sabit olan F# değer türleri için işaretçi this bir inrefolarak çıkarılır.

Bu kuralların tümü birlikte bir inref işaretçinin sahibinin işaret edilen belleğin hemen içeriğini değiştiremediğini gösterir.

Outref semantiği

amacı outref<'T> , işaretçinin yalnızca öğesine yazılması gerektiğini belirtmektir. Beklenmedik şekilde, outref<'T> adına rağmen temel alınan değerin okunmasını sağlar. Bu, uyumluluk amaçlıdır.

Tek bir fark dışında, diğer bir deyişle, outref<'T> değerinden byref<'T>farklı değildir: parametreleri olan yöntemler, bir parametreyle outref<'T>[<Out>] bir yöntemi çağırırken olduğu gibi örtük olarak bir tanımlama grubu dönüş türüne oluşturulur.

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"

C ile birlikte çalışma#

C# ve anahtar sözcüklerinin in refout ref yanı sıra ref dönüşleri de destekler. Aşağıdaki tabloda, F# öğesinin C# tarafından nelerin yaydığı gösterilmektedir:

C# yapısı F# çıkarımları
ref dönüş değeri outref<'T>
ref readonly dönüş değeri inref<'T>
in ref Parametre inref<'T>
out ref Parametre outref<'T>

Aşağıdaki tabloda F# tarafından nelerin yaydığı gösterilmektedir:

F# yapısı Yayılan yapı
inref<'T> bağımsız değişkeni [In] bağımsız değişkende özniteliği
inref<'T> Dönüş modreq değerde özniteliği
inref<'T> soyut yuvada veya uygulamada modreq bağımsız değişkende veya dönüşte
outref<'T> bağımsız değişkeni [Out] bağımsız değişkende özniteliği

Tür çıkarımı ve aşırı yükleme kuralları

inref<'T> Aşağıdaki durumlarda F# derleyicisi tarafından bir tür çıkarılır:

  1. Özniteliği olan bir IsReadOnly .NET parametresi veya dönüş türü.
  2. this Değiştirilebilir alanları olmayan bir yapı türü üzerindeki işaretçi.
  3. Başka inref<_> bir işaretçiden türetilen bellek konumunun adresi.

örtük bir adresi inref alındığında, türünde bağımsız değişken içeren bir aşırı yükleme, türü SomeType bağımsız değişkeni inref<SomeType>olan bir aşırı yüklemeye tercih edilir. Örneğin:

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)

Her iki durumda da, aşırı System.DateTime yüklemeler yerine aşırı inref<System.DateTime>yüklemeler çözülür.

Byref benzeri yapılar

Üçlüye byref//inrefoutref ek olarak, benzer semantiklere byrefbağlı kalabilecek kendi yapılarınızı tanımlayabilirsiniz. Bu işlem şu öznitelikle IsByRefLikeAttribute yapılır:

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 anlamına Structgelmez. Her ikisi de türünde bulunmalıdır.

F# dilindeki "byrefbenzer" yapı, yığına bağlı bir değer türüdür. Yönetilen yığında hiçbir zaman ayrılmaz. byrefBenzer bir yapı, yüksek performanslı programlama için kullanışlıdır, yaşam süresi ve yakalama olmayanlar hakkında güçlü denetimler kümesiyle zorunlu kılınır. Kurallar şunlardır:

  • İşlev parametreleri, yöntem parametreleri, yerel değişkenler, yöntem dönüşleri olarak kullanılabilirler.
  • Statik veya bir sınıfın veya normal yapının örnek üyeleri olamazlar.
  • Bunlar herhangi bir kapatma yapısı (async yöntemler veya lambda ifadeleri) tarafından yakalanamaz.
  • Genel parametre olarak kullanılamazlar.

Bu son nokta, giriş türlerini parametreleştiren genel bir işlev olduğu gibi |> F# işlem hattı stili programlama için de çok önemlidir. Bu kısıtlama, satır içi olduğundan ve gövdesinde satır içi olmayan genel işlevlere çağrı yapmadığından gelecekte için gevşetilebilir |> .

Bu kurallar kullanımı güçlü bir şekilde kısıtlasa da, yüksek performanslı bilgi işlem sözünü güvenli bir şekilde yerine getirmek için bunu yapar.

Byref döndürür

F# işlevlerinden veya üyelerinden Byref dönüşleri üretilebilir ve kullanılabilir. -dönüş yöntemi kullanıldığında byref, değer örtük olarak başvurulmaz. Örneğin:

let squareAndPrint (data : byref<int>) =
    let squared = data*data    // data is implicitly dereferenced
    printfn $"%d{squared}"

Bir değer byref döndürmek için, değeri içeren değişkenin geçerli kapsamdan daha uzun yaşaması gerekir. Ayrıca byref &value döndürmek için kullanın (burada değer geçerli kapsamdan daha uzun süre yaşayan bir değişkendir).

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.

Birden çok zincirleme çağrıdan bir başvuru geçirme gibi örtük başvurudan kaçınmak için kullanın &x (burada x değerdir).

Ayrıca doğrudan bir iadeye byrefde atayabilirsiniz. Aşağıdaki (son derece zorunlu) programı göz önünde bulundurun:

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

Bu çıktı.

Original sequence: 1 3 7 15 31 63 127 255 511 1023
New sequence:      1 3 7 30 31 63 127 255 511 1023

Byrefs için kapsam belirleme

letİlişkili bir değer, başvurusunun tanımlandığı kapsamı aşmasına neden olamaz. Örneğin, aşağıdakilere izin verilmiyor:

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!
    ()

Bu, iyileştirmelerle derleyip derlemediğinize bağlı olarak farklı sonuçlar almanızı önler.