F# 有两个主要功能领域,用于处理低级别编程的空间:
- 这些byref//inrefoutref类型是托管指针。 它们对用法有限制,因此无法在运行时编译无效的程序。
- 类似byref结构,它是一种结构,其语义与编译时限制byref<'T>相同。 一个示例是 Span<T>。
语法
// 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 和 outref
有三种 byref形式:
- 
              inref<'T>,用于读取基础值的托管指针。
- 
              outref<'T>,用于写入基础值的托管指针。
- 
              byref<'T>,用于读取和写入基础值的托管指针。
可以传递预期inref<'T>位置的 Abyref<'T>。 同样,可以传递预期outref<'T>位置的 Abyref<'T>。
Using byrefs
若要使用 a inref<'T>,需要使用以下项 &获取指针值:
open System
let f (dt: inref<DateTime>) =
    printfn $"Now: %O{dt}"
let usage =
    let dt = DateTime.Now
    f &dt // Pass a pointer to 'dt'
若要使用 outref<'T> 或 byref<'T>写入指针,还必须使获取指向 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
如果只是编写指针而不是读取指针,请考虑使用 outref<'T> 而不是 byref<'T>。
Inref 语义
请考虑以下代码:
let f (x: inref<SomeStruct>) = x.SomeField
从语义上讲,这意味着以下内容:
- 指针的 x持有者只能使用它来读取值。
- 获取到 struct嵌套在内SomeStruct字段的任何指针都属于给定类型inref<_>。
以下内容也属实:
- 没有任何意义,其他线程或别名没有写入访问权限 x。
- 没有意义, SomeStruct由于x是一个inref不可变的。
但是, 对于不可变 的 F# 值类型,指针 this 被推断为一个 inref。
所有这些规则都意味着指针的 inref 持有者不能修改所指向的内存的直接内容。
Outref 语义
              outref<'T>目的是指示只应将指针写入到该指针。 意外的是, outref<'T> 允许读取基础值,尽管其名称。 这是出于兼容性目的。
从语义上讲, outref<'T> 除了一个区别之外,除了 byref<'T>一个区别:具有 outref<'T> 参数的方法被隐式构造为元组返回类型,就像使用参数调用方法时一 [<Out>] 样。
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 的互作#
除返回外,refC# 还支持in ref和out ref关键字。 下表显示了 F# 如何解释 C# 发出的内容:
| C# 构造 | F# 推理 | 
|---|---|
| ref返回值 | outref<'T> | 
| ref readonly返回值 | inref<'T> | 
| in ref参数 | inref<'T> | 
| out ref参数 | outref<'T> | 
下表显示了 F# 发出的内容:
| F# 构造 | 发出的构造 | 
|---|---|
| inref<'T>参数 | [In]参数上的属性 | 
| inref<'T>返回 | modreq值上的属性 | 
| inref<'T>在抽象槽或实现中 | modreqon 参数或返回 | 
| outref<'T>参数 | [Out]参数上的属性 | 
类型推理和重载规则
              inref<'T>以下情况由 F# 编译器推断类型:
- 具有 IsReadOnly特性的 .NET 参数或返回类型。
- 
              this没有可变字段的结构类型的指针。
- 从另一个 inref<_>指针派生的内存位置的地址。
当采用隐式地址时,具有类型SomeType参数的inref重载优先于具有类型inref<SomeType>参数的重载。 例如:
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)
在这两种情况下,都解决了占用System.DateTime的重载,而不是重载。inref<System.DateTime>
类似 Byref 的结构
除了byref//inrefoutref三者,还可以定义可以遵循byref类似语义的结构。 这是使用 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 并不暗示 Struct。 两者必须存在于类型上。
F# 中的“类似”byref结构是堆栈绑定值类型。 它永远不会在托管堆上分配。 
              byref类似结构对于高性能编程非常有用,因为它通过一组关于生存期和非捕获的强检查强制执行。 规则包括:
- 它们可用作函数参数、方法参数、局部变量、方法返回。
- 它们不能是类或普通结构的静态成员或实例成员。
- 它们不能由任何关闭构造(async方法或 lambda 表达式)捕获。
- 它们不能用作泛型参数。
- 从 F# 9 开始,如果使用允许构造反约束在 C# 中定义泛型参数,则会放宽此限制。 F# 可以使用类似 byref 的类型和方法实例化此类泛型。 作为一些示例,这会影响 BCL 委托类型(、)、接口(Action<>IEnumerable<>、IComparable<>)以及具有用户提供的累加器函数的泛型参数(String.string Create<TState>(int length, TState state, SpanAction<char, TState> action))。Func<>
- 不可能在 F# 中创作支持类似 byref 的类型的泛型代码。
 
- 从 F# 9 开始,如果使用允许构造反约束在 C# 中定义泛型参数,则会放宽此限制。 F# 可以使用类似 byref 的类型和方法实例化此类泛型。 作为一些示例,这会影响 BCL 委托类型(、)、接口(
最后一点对于 F# 管道样式编程至关重要,一 |> 个参数化其输入类型的泛型函数也至关重要。 将来可能会放宽 |> 此限制,因为它是内联的,并且不会对其正文中的非内联泛型函数进行任何调用。
尽管这些规则强烈限制使用,但它们这样做是以安全的方式履行高性能计算的承诺。
Byref 返回
可以生成和使用 F# 函数或成员的 Byref 返回。 使用 byref-returning 方法时,将隐式取消引用该值。 例如:
let squareAndPrint (data : byref<int>) =
    let squared = data*data    // data is implicitly dereferenced
    printfn $"%d{squared}"
若要返回值 byref,包含该值的变量必须比当前范围长。
此外,若要返回 byref,请使用 &value (其中值是生存时间超过当前范围的变量)。
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.
若要避免隐式取消引用(例如通过多个链接调用传递引用),请使用 &x (其中 x 为值)。
还可以直接分配给返回 byref。 请考虑以下(高度强制性)程序:
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
以下是输出:
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 的范围
              let绑定值不能使其引用超出其定义范围。 例如,不允许执行以下作:
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!
    ()
这可以防止你获得不同的结果,具体取决于是否使用优化进行编译。