Понимание byref, ref и &
Ну, я понял, что F # умеет управлять ссылками (как-то вроде ссылок на С++). Это позволяет изменять значение параметров, передаваемых в функции, а также позволяет программисту возвращать более одного значения.
Однако вот что мне нужно знать:
-
Ссылка на ключевое слово: Ключевое слово ref
используется для создания из значения ссылки на это значение выводимого типа. Так
let myref = ref 10
Это означает, что F # создаст объект типа Ref<int>
, поместив туда (в изменяемое поле) my int 10
.
OK. Поэтому я предполагаю, что ref
используется для создания экземпляров типа Ref<'a>
. Правильно ли это?
-
Значение доступа: для доступа к значению, хранящемуся в ссылке, я могу сделать это:
let myref = ref 10
let myval = myref.Value
let myval2 = !myref
В то время как оператор :=
позволяет мне изменить значение, подобное этому:
let myref = ref 10
myref.Value <- 30
myref := 40
Итак !
(Bang) разыгрывает мою ссылку. И :=
отредактируйте его. Я полагаю, это тоже правильно.
-
Оператор и оператор: что делает этот оператор? Должен ли он применяться к эталонному типу? Нет, я думаю, это должно быть применено к изменяемому значению, и это возвращает что? Ссылка? Адрес? Если вы используете интерактивный интерфейс:
let mutable mutvar = 10;;
&a;;
Последняя строка вызывает ошибку, поэтому я не понимаю, для чего предназначен оператор &
.
-
ByRef: Как насчет byref
? Это очень важно для меня, но я понимаю, что не понимаю.
Я понимаю, что он используется в функции для передачи параметров. Один использует byref, когда хочет, чтобы переданное значение можно было отредактировать (это немного противоречит философии функциональных языков, но f # - это нечто большее). Рассмотрим следующее:
let myfunc (x: int byref) =
x <- x + 10
Это странно. Я знаю, что если у вас есть ссылка let myref = ref 10
, а затем сделайте это, чтобы изменить значение: myref <- 10
возникает ошибка, потому что она должна быть такой: myref := 10
. Однако тот факт, что в этой функции я могу редактировать x
с помощью оператора <-
, означает, что x
не является ссылкой, правильно?
Если я предполагаю, что x
не является ссылкой, я также предполагаю, что в функциях при использовании параметра byref
для параметра этот параметр может иметь применяемый mutable синтаксис. Так что это всего лишь вопрос синтаксиса, если я предполагаю, что это нормально, и на самом деле все работает (ошибок компилятора нет). Однако что такое x
?
-
Функции вызова: как я могу использовать функцию, используя параметры byref?
Используется оператор &
, но не могли бы вы объяснить это лучше, пожалуйста? В этой статье: Параметры и аргументы MSDN приведен следующий пример:
type Incrementor(z) =
member this.Increment(i : int byref) =
i <- i + z
let incrementor = new Incrementor(1)
let mutable x = 10
// A: Not recommended: Does not actually increment the variable. (Me: why?)
incrementor.Increment(ref x)
// Prints 10.
printfn "%d" x
let mutable y = 10
incrementor.Increment(&y) (* Me: & what does it return? *)
// Prints 11.
printfn "%d" y
let refInt = ref 10
incrementor.Increment(refInt) (* Why does it not work in A, but here it does? *)
// Prints 11.
printfn "%d" !refInt
Ответы
Ответ 1
ключевое слово Ref Да, когда вы пишете let a = ref 10
, вы в основном пишете let a = new Ref<int>(10)
, где тип Ref<T>
имеет изменяемое поле Value
.
Значение доступа Операторы :=
и !
являются просто ярлыками для записи:
a.Value <- 10 // same as writing: a := 10
a.Value // same as writing: !a
ByRef - это особый тип, который может (разумно) использоваться только в параметрах метода. Это означает, что аргумент должен быть по существу указателем на некоторую ячейку памяти (выделенную в куче или стеке). Он соответствует модификаторам out
и ref
в С#. Обратите внимание: вы не можете создать локальную переменную этого типа.
Оператор и - это способ создания значения (указателя), которое может быть передано как аргумент функции/методу, ожидающему тип byref
.
Функции вызова пример с byref
работает, потому что вы передаете методу ссылку на локальную изменяемую переменную. С помощью ссылки метод может изменить значение, сохраненное в этой переменной.
Не работает следующее:
let a = 10 // Note: You don't even need 'mutable' here
bar.Increment(ref a)
Причина в том, что вы создаете новый экземпляр Ref<int>
, и вы копируете значение a
в этот экземпляр. Затем метод Increment
изменяет значение, сохраненное в куче в экземпляре Ref<int>
, но у вас больше нет ссылки на этот объект.
let a = ref 10
bar.Increment(a)
Это работает, потому что a
- это значение типа Ref<int>
, и вы передаете указатель на выделенный в кучу экземпляр на Increment
, а затем получите значение из ячейки с выделенной кучей, используя !a
.
(Вы можете использовать значения, созданные с помощью ref
в качестве аргументов для byref
, потому что компилятор обрабатывает этот случай специально - он автоматически примет ссылку в поле Value
, потому что это полезный сценарий...).