在我的代码中,我通过引用传递一些结构,声明它们是可变的并使用&
符号。问题是,在某些地方,字段已损坏(仅在发布模式下发生),我不知道为什么。
我找到了一个修复,使用ref关键字而不是address-of运算符。我知道您可以自由地交换它们(如果是实例成员参数),但为什么它可以解决我的问题?
这是一个小代码示例,说明了这一点:
[<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个字节左右)?
谢谢!
答案 0 :(得分:5)
在您的示例中使用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
直接修改堆栈上的值。