Megosztás a következőn keresztül:


Byrefs

Az F# két fő funkcióterülettel rendelkezik, amelyek az alacsony szintű programozás területén foglalkoznak:

  • Azok a byref//inrefoutref típusok, amelyek felügyelt mutatók. A használat korlátozásokkal rendelkezik, így futásidőben nem állítható össze érvénytelen program.
  • A byref-like struct, amely egy olyan szerkezet , amely hasonló szemantikával és ugyanolyan fordítási időkorlátokkal rendelkezik, mint byref<'T>a . Ilyen például a .Span<T>

Syntax

// 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 és outref

A következők három formája byreflétezik:

  • inref<'T>, egy felügyelt mutató az alapul szolgáló érték olvasásához.
  • outref<'T>, egy felügyelt mutató a mögöttes értékre való íráshoz.
  • byref<'T>, egy felügyelt mutató a mögöttes érték olvasásához és írásához.

A byref<'T> továbbítható, ahol egy inref<'T> várható. Hasonlóképpen, átadható egy byref<'T> olyan érték, ahol a outref<'T> várt érték szerepel.

Byrefs használata

Ha egy inref<'T>mutatót szeretne használni, a következővel kell egy mutatóértéket &lekérnie:

open System

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

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

Ha egy vagy byref<'T>több outref<'T> elemet használva szeretne írni a mutatóra, akkor azt az értéket is meg kell adnia, amelybe mutablemutatót fog.

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

Ha csak az egérmutatót írja ahelyett, hogy elolvassa, fontolja meg a helyett a byref<'T>használatátoutref<'T>.

Inref szemantikák

Tekintse meg az alábbi kódot:

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

Szemantikailag ez a következőket jelenti:

  • A mutató tulajdonosa x csak az érték olvasására használhatja.
  • A beágyazott mezőkre struct beolvasott SomeStruct egérmutatók típust inref<_>kapnak.

A következő is igaz:

  • Nincs jelentősége annak, hogy más szálak vagy aliasok nem rendelkeznek írási hozzáféréssel x.
  • Nincs olyan hatása, hogy SomeStruct nem módosítható, mert az, x hogy egy inref.

A nem módosítható F#-értéktípusok esetében azonban a this mutató inrefegy .

Ezek a szabályok együttesen azt jelentik, hogy a mutató tulajdonosa inref nem módosíthatja a mutatott memória közvetlen tartalmát.

Outref szemantikák

A cél outref<'T> az, hogy jelezze, hogy a mutatót csak erre kell írni. Váratlanul lehetővé teszi a outref<'T> mögöttes érték olvasását a neve ellenére. Ez kompatibilitási célokat szolgál.

Szemantikailag nem más, outref<'T> mint byref<'T>egy különbség: a paraméterekkel rendelkező outref<'T> metódusok implicit módon egy tuple visszatérési típusba vannak felépítve, ugyanúgy, mint amikor egy metódust paraméterrel [<Out>] hívnak meg.

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 with C#

A C# a visszatérés mellett a kulcsszavakat és out ref a in ref kulcsszavakat ref is támogatja. Az alábbi táblázat bemutatja, hogyan értelmezi az F# a C# által kibocsátott adatokat:

C#-szerkezet F#-következtetések
ref visszatérési érték outref<'T>
ref readonly visszatérési érték inref<'T>
in ref Paraméter inref<'T>
out ref Paraméter outref<'T>

Az alábbi táblázat azt mutatja be, hogy az F# mit bocsát ki:

F#-szerkezet Kibocsátott szerkezet
inref<'T> argumentum [In] attribútum az argumentumon
inref<'T> Vissza modreq attribútum az értéken
inref<'T> absztrakciós pontban vagy implementációban modreq argumentumon vagy visszatérésen
outref<'T> argumentum [Out] attribútum az argumentumon

Következtetési és túlterhelési szabályok beírása

Az inref<'T> F#-fordító a következő esetekben következtet egy típusra:

  1. Attribútummal rendelkező IsReadOnly .NET-paraméter vagy visszatérési típus.
  2. Egy this olyan szerkezettípus mutatója, amely nem tartalmaz módosítható mezőket.
  3. Egy másik inref<_> mutatóból származtatott memóriahely címe.

Amikor egy implicit címet inref vesz fel, a típus SomeType argumentumával rendelkező túlterhelést előnyben részesíti a túlterhelés egy típus inref<SomeType>argumentumával. Példa:

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)

Mindkét esetben a túlterhelések a System.DateTime túlterhelések inref<System.DateTime>helyett feloldódnak.

Byref-szerű szerkezetek

A trió mellett byref//inrefoutref saját szerkezeteket is meghatározhat, amelyek megfelelnek a byref-szerű szemantikának. Ez a következő attribútummal IsByRefLikeAttribute történik:

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 nem azt jelenti , hogy Struct. Mindkettőnek jelen kell lennie a típuson.

Az F# "byref-szerű" szerkezete veremhez kötött értéktípus. A rendszer soha nem foglalja le a felügyelt halomra. A byref-like struct hasznos a nagy teljesítményű programozáshoz, mivel erős ellenőrzésekkel kényszerítik ki az élettartamra és a nem rögzítésre vonatkozóan. A szabályok a következők:

  • Használhatók függvényparaméterekként, metódusparaméterekként, helyi változókként, metódusvisszatérésekként.
  • Nem lehetnek statikusak vagy példányok tagjai egy osztálynak vagy normál szerkezetnek.
  • Nem rögzíthetők lezáró szerkezettel (async metódusokkal vagy lambdakifejezésekkel).
  • Ezek nem használhatók általános paraméterként.

Ez az utolsó pont kulcsfontosságú az F# folyamatstílusú programozáshoz, csakúgy, mint |> egy általános függvény, amely paraméterezi a bemeneti típusokat. Ez a korlátozás a jövőben enyhíthető |> , mivel beágyazott, és nem indít hívásokat a nem beágyazott általános függvényekre a szervezetben.

Bár ezek a szabályok erősen korlátozzák a használatot, azért teszik ezt, hogy biztonságosan teljesítsék a nagy teljesítményű számítástechnika ígéretét.

Byref visszaadja

Az F# függvényekből vagy tagokból származó Byref-visszatérések előállíthatók és felhasználhatók. -returning metódus használata byrefesetén az érték implicit módon hareferens lesz. Példa:

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

Az érték byref visszaadásához az értéket tartalmazó változónak hosszabb ideig kell élnie, mint az aktuális hatókör. A byref visszaadásához használja &value is (ahol az érték egy olyan változó, amely hosszabb ideig él, mint az aktuális hatókör).

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.

Az implicit hareferencia elkerülése érdekében, például ha több láncolt híváson keresztül továbbít egy hivatkozást, használja &x (hol x van az érték).

A visszatéréshez byrefközvetlenül is hozzárendelhet . Fontolja meg a következő (rendkívül imperatív) programot:

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

Ez a kimenet:

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

Hatókörkezelés byrefs esetén

A letkötött értékek hivatkozása nem haladhatja meg azt a hatókört, amelyben definiálták. A következők például nem engedélyezettek:

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

Ez megakadályozza, hogy különböző eredményeket kap, attól függően, hogy optimalizálással fordít-e vagy sem.