Различия между операторами адреса и ссылки - PullRequest
1 голос
/ 23 октября 2010

В моем коде я передаю некоторые структуры по ссылке, объявляя их изменяемыми и используя символ &.Проблема в том, что в некоторых местах поля повреждены (это происходит только в режиме выпуска), и я не знаю точно, почему.

Я нашел исправление, используя ключевое слово 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 Ответ

5 голосов
/ 23 октября 2010

Использование 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, напрямую изменяет значение в стеке.

...