address-of和ref运算符之间的差异

时间:2010-10-23 14:59:55

标签: f# struct operators byref

在我的代码中,我通过引用传递一些结构,声明它们是可变的并使用&符号。问题是,在某些地方,字段已损坏(仅在发布模式下发生),我不知道为什么。

我找到了一个修复,使用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个字节左右)?

谢谢!

1 个答案:

答案 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直接修改堆栈上的值。