Byrefs
F# alt düzey programlama alanıyla ilgilenen iki önemli özellik alanına sahiptir:
- yönetilen işaretçiler olan
byref
/inref
/outref
türleri. Çalışma zamanında geçersiz olan bir programı derleyemezsiniz diye kullanım kısıtlamaları vardır. - benzer semantiklere ve
byref<'T>
ile aynı derleme zamanı kısıtlamalarına sahip bir yapı olanbyref
benzeri bir yapı. Örnek olarak 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
üç byref
biçimi vardır:
-
inref<'T>
, temel değeri okumak için yönetilen bir işaretçidir. -
outref<'T>
, temel alınan değere yazmak için bir yönetilen işaretçidir. -
byref<'T>
, altında yatan değeri okumak ve yazmak için yönetilen bir işaretçidir.
byref<'T>
'nın yerine inref<'T>
'in beklendiği yerde geçirilebilir. Benzer şekilde, outref<'T>
beklendiği yerde bir byref<'T>
geçirilebilir.
Byrefs'i kullanma
inref<'T>
kullanmak 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'
Bir outref<'T>
veya byref<'T>
kullanarak işaretçiye yazmak için, aynı zamanda elde ettiğiniz değeri mutable
için bir işaretçi haline getirmeniz gerekmektedir.
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, byref<'T>
yerine outref<'T>
kullanmayı düşünün.
Inref semantiği
Aşağıdaki kodu göz önünde bulundurun:
let f (x: inref<SomeStruct>) = x.SomeField
Bu, anlam olarak şu anlama gelir:
-
x
işaretçisinin tutucusu yalnızca değeri okumak için kullanabilir. -
SomeStruct
iç içe yerleştirilmişstruct
alanlara alınan tüm işaretçilereinref<_>
türü verilir.
Aşağıdakiler de geçerlidir:
- Diğer iş parçacıklarının veya diğer adların
x
yazma erişimine sahip olmadığını gösteren bir sonuç yoktur. -
SomeStruct
'ın değiştirilmez olduğu söylenemez çünküx
birinref
'dir.
Ancak, sabit olan F# değer türleri için, this
işaretçisi inref
olarak çıkarılır.
Bu kuralların tümü birlikte, bir inref
işaretçisinin sahibinin işaret edilen belleğin hemen içeriğini değiştiremeyeceği anlamına gelir.
Outref semantiği
outref<'T>
'ın amacı, işaretçinin yalnızca üzerine yazılabileceğini belirtmektir. Beklenmedik şekilde, outref<'T>
adına rağmen temel alınan değerin okunmasına izin verir. Bu, uyumluluk amaçlıdır.
Semantik olarak, outref<'T>
'un byref<'T>
'den tek farkı şudur: outref<'T>
parametreleri içeren yöntemler, tıpkı [<Out>]
parametresine sahip bir yöntemi çağırırken olduğu gibi, örtük olarak tanımlama grubu dönüş türüne dönüştürülür.
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# in ref
ve out ref
anahtar sözcüklerinin yanı sıra ref
dönüşlerini de destekler. Aşağıdaki tabloda, C# dilinin ürettiklerini F# dilinin nasıl yorumladığı gösterilmektedir.
C# dil yapısı | F# çıkarım yapar |
---|---|
ref dönüş değeri |
outref<'T> |
ref readonly dönüş değeri |
inref<'T> |
in ref parametresi |
inref<'T> |
out ref parametresi |
outref<'T> |
Aşağıdaki tablo, F#'ın ürettiklerini gösteriyor.
F# yapısı | Yayılan yapılandırma |
---|---|
inref<'T> bağımsız değişkeni |
Argümandaki [In] özniteliği |
inref<'T> iade |
Değer üzerinde modreq özniteliği |
Soyut yuva veya uygulamada inref<'T> |
modreq bağımsız değişken veya dönüş üzerinde |
outref<'T> bağımsız değişkeni |
Bağımsız değişkende [Out] özniteliği |
Tür çıkarımı ve aşırı yükleme kuralları
Aşağıdaki durumlarda F# derleyicisi tarafından bir inref<'T>
türü çıkarılır:
-
IsReadOnly
özniteliğine sahip bir .NET parametresi veya dönüş türü. - Değiştirilebilir alanları olmayan bir yapı türü üzerindeki
this
işaretçisi. - Başka bir
inref<_>
işaretçisinden türetilen bellek konumunun adresi.
Bir inref
adresi örtük olarak alındığında, SomeType
türünde bir bağımsız değişkene sahip aşırı yükleme, inref<SomeType>
türünde bir bağımsız değişkene sahip 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 System.DateTime
alan aşırı yüklemeler, inref<System.DateTime>
alan aşırı yüklemeler yerine çözülür.
Byref benzeri yapılar
byref
/
inref
/
outref
trio'ya ek olarak, byref
benzeri semantiklere bağlı kalabilecek kendi yapılarınızı tanımlayabilirsiniz. Bu işlem IsByRefLikeAttribute özniteliğiyle 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
Struct
anlamına gelmez. Her ikisi de tipinde bulunmalıdır.
F# dilinde "byref
-like" yapısı, yığına bağlı bir değer türüdür. Yönetilen yığında hiçbir zaman tahsis edilmez.
byref
gibi bir yapı, ömrü ve yakalanmama konusunda güçlü denetimlerle desteklendiğinden yüksek performanslı programlamada faydalıdı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öntemleri veya lambda ifadeleri) tarafından yakalanamaz. - Genel parametre olarak kullanılamazlar.
- F# 9'dan itibaren, genel parametre C# dilinde ref struct sınırlaması kaldırarak tanımlanırsa bu kısıtlama gevşetilir. F#, byref benzeri tiplerle tip ve metotlarda bu tür genel tiplerin örneğini oluşturabilir. Birkaç örnek olarak, bu BCL temsilci türlerini (
Action<>
,Func<>
), arabirimleri (IEnumerable<>
,IComparable<>
) ve kullanıcı tarafından sağlanan bir biriktirici işleviyle (String.string Create<TState>(int length, TState state, SpanAction<char, TState> action)
) genel bağımsız değişkenleri etkiler. - F# dilinde byref benzeri türleri destekleyen genel kod yazmak mümkün değildir.
- F# 9'dan itibaren, genel parametre C# dilinde ref struct sınırlaması kaldırarak tanımlanırsa bu kısıtlama gevşetilir. F#, byref benzeri tiplerle tip ve metotlarda bu tür genel tiplerin örneğini oluşturabilir. Birkaç örnek olarak, bu BCL temsilci türlerini (
|>
giriş türlerini parametreleştiren genel bir işlev olduğundan, bu son nokta F# işlem hattı stili programlama için çok önemlidir. Bu kısıtlama, satır içi olduğu ve gövdesinde satır içi olmayan genel fonksiyonlara çağrıda bulunmadığı için 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.
byref
-dönüş yöntemi kullanıldığında, değer örtük olarak işlenir. Mesela:
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 döndürmek için &value
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 zincirli çağrıdan referans geçirme gibi örtük dereferansı önlemek için &x
kullanın (burada x
değerdir).
Ayrıca doğrudan bir dönüş etiketi byref
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
Çıkış şu şekildedir:
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
bağlı bir değerin, referansının tanımlandığı kapsamı aşması mümkün değildir. Ö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.