我正在尝试在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
无法使用以下错误进行编译:
但是,如果我将代码修改为以下内容(请注意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)
预计会出现这种情况吗?如果是这样,有人可以解释它背后的推理......
答案 0 :(得分:7)
原因是byref<'T>
不是.NET中的真实类型。 F#使用它来表示通过ref
和out
参数传递的值,但它不是可能出现在程序中任何位置的普通类型。
F#限制了它们的使用范围 - 你只能将它们用于局部变量(基本上传递一个引用或指针),你可以将它们用作方法参数(然后编译器可以将它编译为方法参数)。
使用curried方法,编译器会生成一个返回函数值的属性,因此(在封面下),您可以获得类似PostInvoke
类型的属性FSharpFunc<T1, FSharpFunc<T2, T3>>
。在这里,T1
或T2
不能是byref<T>
类型,因为byref
不是真正的.NET类型。这就是为什么curried方法不能有byref
个参数。
您可以看到这种情况的另一种情况是,例如,您尝试创建byref
值列表:
let foo () =
let a : list<byref<int>> = []
a
在这里:
错误FS0412:类型实例化涉及byref类型。 Common IL的规则不允许这样做。