在curried的参数中通过引用传递参数

时间:2015-10-23 16:42:10

标签: f#

我正在尝试在F#中实现({WCF成名的)接口IDispatchMessageInspector

open System.ServiceModel.Dispatcher
open System.ServiceModel.Channels

type ServiceInterceptor() as interceptor = 

    abstract member PreInvoke : byref<Message> -> obj
    abstract member PostInvoke : byref<Message> -> obj -> unit

    default x.PreInvoke m = null
    default x.PostInvoke m s = ()

    interface IDispatchMessageInspector with
         member x.AfterReceiveRequest(request, channel, instanceContext) = interceptor.PreInvoke(&request)
         member x.BeforeSendReply(reply : byref<Message>, correlationState) = interceptor.PostInvoke &reply correlationState

无法使用以下错误进行编译:

enter image description here

但是,如果我将代码修改为以下内容(请注意PostInvoke中签名的更改),一切正常:

open System.ServiceModel.Dispatcher
open System.ServiceModel.Channels

type ServiceInterceptor() as interceptor = 

    abstract member PreInvoke : byref<Message> -> obj
    abstract member PostInvoke : byref<Message> * obj -> unit

    default x.PreInvoke m = null
    default x.PostInvoke (m, s) = ()

    interface IDispatchMessageInspector with
         member x.AfterReceiveRequest(request, channel, instanceContext) = interceptor.PreInvoke(&request)
         member x.BeforeSendReply(reply : byref<Message>, correlationState) = interceptor.PostInvoke(&reply, correlationState)

预计会出现这种情况吗?如果是这样,有人可以解释它背后的推理......

1 个答案:

答案 0 :(得分:7)

原因是byref<'T>不是.NET中的真实类型。 F#使用它来表示通过refout参数传递的值,但它不是可能出现在程序中任何位置的普通类型。

F#限制了它们的使用范围 - 你只能将它们用于局部变量(基本上传递一个引用或指针),你可以将它们用作方法参数(然后编译器可以将它编译为方法参数)。

使用curried方法,编译器会生成一个返回函数值的属性,因此(在封面下),您可以获得类似PostInvoke类型的属性FSharpFunc<T1, FSharpFunc<T2, T3>>。在这里,T1T2不能是byref<T>类型,因为byref不是真正的.NET类型。这就是为什么curried方法不能有byref个参数。

您可以看到这种情况的另一种情况是,例如,您尝试创建byref值列表:

let foo () =
  let a : list<byref<int>> = []
  a

在这里:

  

错误FS0412:类型实例化涉及byref类型。 Common IL的规则不允许这样做。