2

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

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

谢谢!

4

1 回答 1

6

在你的例子中使用的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是一个函数,它接受一个值并创建一个堆分配的参考单元。在第二个示例中,类型nref<int>,即参考单元格。F# 允许您将引用单元格作为参数传递给byref参数 - 在这种情况下,它会创建一个指向堆分配(引用单元格)对象的字段的指针。

在第二个示例中,我们创建引用单元格,将指向引用单元格的指针传递给modify函数,然后使用!<ref>语法从引用单元格中获取值。在第一个示例中,我们在调用modify函数时创建了一个新的引用单元格(并且 的值n从堆栈复制到堆分配的单元格)。调用后不使用单元格(并且值n保持 15)

另一方面,mutable变量只是简单地存储在堆栈中并且可以改变。主要区别在于ref总是堆分配 - 使用mutable可能(原则上)更快一点,因为调用的函数byref直接修改堆栈上的值。

于 2010-10-23T15:30:21.290 回答