在正常情况下,F#函数可以通过调用new DelegateType
并将函数作为参数传入来转换为委托。但是,当委托包含byref
参数时,这是不可能直接实现的。例如代码:
type ActionByRef<'a> = delegate of 'a byref -> unit
let f (x:double byref) =
x <- 6.0
let x = ref 42.0
let d = new ActionByRef<_>(f)
不会编译,给出以下错误:
此函数值用于构造其签名包含byref参数的委托类型。必须使用带1个参数的显式lambda表达式。
出现错误后,修改代码以使用
let d = new ActionByRef<_>(fun x -> f(&x))
工作。但我的问题是:为什么这是必要的?为什么F#不允许从命名函数转换到此委托,但从lambda转换是可以的?
我在研究另一个问题时发现了这种行为。我意识到byref
只是为了与其他.Net语言兼容。
我认为问题是byref<'T>
在F#中不是一个实际的类型——它看起来像一个类型(为了简化语言),但它被编译为一个标记有out
标志的参数。这意味着byref<'T>
只能在编译器可以实际使用out
标志的地方使用。
函数值的问题在于,您可以构造函数,例如通过部分应用程序:
let foo (n:int) (b:byref<int>) =
b <- n
当您将foo
作为参数传递给委托构造函数时,这是部分应用程序(没有参数)的特定情况,但部分应用程序实际上需要构造一个新方法,然后将其提供给委托:
type IntRefAction = delegate of byref<int> -> unit
let ac = IntRefAction(foo 5)
编译器可能很聪明,可以用byref
参数(或out
标志)生成新方法,然后通过引用实际函数来传递,但通常情况下,如果不使用fun ... -> ...
语法,还会有其他编译器生成的方法。处理这种情况会增加复杂性,我认为这是一种相对罕见的情况,所以F#编译器不会这样做,并要求你更明确。。。