28

好吧,我开始明白 F# 能够管理引用(某种 C++ 之类的引用)。这使得改变函数中传递的参数的值成为可能,并且还使程序员能够返回多个值。但是,这是我需要知道的:

  1. Ref 关键字:该关键字ref用于从一个值创建对推断类型的该值的引用。所以

    let myref = ref 10
    

    这意味着 F# 将创建一个类型的对象Ref<int>放在那里(在可变字段中) my int 10

    好的。所以我假设它ref用于创建Ref<'a>类型的实例。这是正确的吗?

  2. 访问值:为了访问存储在引用中的值,我可以这样做:

    let myref = ref 10
    let myval = myref.Value
    let myval2 = !myref
    

    虽然:=操作员只是让我像这样编辑值:

    let myref = ref 10
    myref.Value <- 30
    myref := 40
    

    所以!(Bang) 取消引用我的参考。并:=编辑它。我想这也是正确的。

  3. & 运算符:这个运算符是做什么的?它是否适用于引用类型?不,我想它必须应用于可变值,这会返回什么?参考资料?地址?如果使用交互式:

    let mutable mutvar = 10;;
    &a;;
    

    最后一行抛出一个错误,所以我不明白&运算符的用途。

  4. ByRef:那byref呢?这对我来说很重要,但我意识到我不明白。我知道它用于有关参数传递的功能。当他希望可以编辑传递的值时使用 byref(这有点违背函数式语言的理念,但 f# 不止于此)。考虑以下:

    let myfunc (x: int byref) =
        x <- x + 10
    

    这很奇怪。我知道,如果你有一个参考let myref = ref 10,然后这样做来编辑值:myref <- 10它会出现一个错误,因为它应该是这样的:myref := 10。但是,在该函数中我可以x使用<-运算符进行编辑这一事实意味着这x不是参考,对吧?

    如果我假设这x不是参考,那么我还假设,在函数中,当byref在参数上使用时,该参数可以应用可变语法。所以这只是语法问题,如果我假设我没问题,而且事实上,一切正常(没有编译器错误)。然而,什么是x

  5. 调用函数:如何使用利用 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
    
4

1 回答 1

32

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是一种特殊类型,只能(合理地)在方法参数中使用。这意味着参数本质上应该是指向某个内存位置(在堆或堆栈上分配)的指针。它对应于 C# 中的outref修饰符。请注意,您不能创建这种类型的局部变量。

& 运算符是一种创建值(指针)的方法,该值可以作为参数传递给期望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字段,因为这是一个有用的场景......)。

于 2011-02-17T11:54:02.020 回答