在我的代码中,我通过引用传递一些结构,声明它们可变并使用 &
符号。问题是在某些地方字段已损坏(仅发生在 Release模式下),我完全不知道原因。
我找到了一个修复方法,使用 ref 关键字而不是地址运算符。我知道您可以自由交换它们(在实例成员参数的情况下),但为什么它解决了我的问题?
这里有一个小代码示例来说明这一点:
[<Struct>]
type MyStruct =
val mutable private i : int
val mutable private f : float
new (a, b) = { i = a; f = b }
member t.I = t.i
member t.F = t.f
type Printer () =
member t.Print(data : MyStruct byref) = printfn "%d %f" data.I data.F
let bar (p : Printer) =
let mutable x = new MyStruct(2, 8.0)
p.Print(&x)
let foo (p : Printer) =
let mutable y = new MyStruct(2, 8.0)
p.Print(ref y) // What is exactly the difference, under the hood?
let main () =
foo (new Printer())
bar (new Printer())
do main ()
使用 byref 传递结构似乎仅适用于互操作场景或如果您想要改变结构的字段。但这不是我的情况。我是否应该考虑按值传递结构类型(大约 20 个字节左右)?
谢谢!
最佳答案
使用ref
在你的例子中可能不完全是你想写的:
let modify (data:int byref) = data <- 10
let foo() =
let mutable n = 15 // Note: Compiles without 'mutable'
modify (ref n)
printfn "%d" n // Prints '10'!!
您可能想要这样的东西:
let foo() =
let n = ref 15
modify n
printfn "%d" (!n) // Prints '15'
那么,有什么区别呢? ref
是一个函数,它接受一个值并创建一个堆分配的引用单元。在第二个示例中,n
的类型是 ref<int>
,这是引用单元格。 F# 允许您将引用单元格作为参数传递给 byref
参数 - 在这种情况下,它创建一个指向堆分配(引用单元)对象的字段的指针。
在第二个示例中,我们创建引用单元格,将指向引用单元格的指针传递给 modify
函数,然后使用!<ref>
从引用单元格获取值的语法。在第一个示例中,我们在调用modify
时创建一个新的引用单元格。函数(并且 n
的值从堆栈复制到堆分配单元)。调用后该单元格不再使用(n
的值保持15)
另一方面,mutable
变量只是存储在堆栈上并且可以改变。主要区别在于ref
始终分配堆 - 使用 mutable
(原则上)可能会快一点,因为调用的函数采用 byref
直接修改堆栈上的值。
关于f# - address-of 和 ref 运算符之间的差异,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4004594/