为什么F#编译器在一种情况下给出错误,而在另一种情况下却没有给出错误?

为什么F#编译器在一种情况下给出错误,而在另一种情况下却没有给出错误?,f#,F#,我正在处理一个来自F#的平台调用,我遇到了一个编译器错误,我真的无法理解。首先,让我展示我正在做的事情的C签名: int Foo( ULONG_PTR *phHandle, DWORD flags ); 在F#中,我认为本机调用此函数的正确方法如下: [<DllImport("somedll.dll")>] static extern int APlatformInvokeCall ( [<Out>]nativeint&

我正在处理一个来自F#的平台调用,我遇到了一个编译器错误,我真的无法理解。首先,让我展示我正在做的事情的C签名:

int Foo(
    ULONG_PTR *phHandle,
    DWORD flags
);
在F#中,我认为本机调用此函数的正确方法如下:

[<DllImport("somedll.dll")>]
static extern int APlatformInvokeCall
    (
        [<Out>]nativeint& phHandle,
        uint32 flags
    )
[]
静态外部int APlatformInvokeCall
(
[]国家和phHandle,
uint32旗帜
)
如果我试图在类中调用此函数,则在这样调用时会出现编译错误:

type Class1() = 
    [<DllImport("somedll.dll")>]
    static extern int APlatformInvokeCall
        (
            nativeint& phHandle,
            uint32 flags
        )

    member this.Foo() =
        let mutable thing = nativeint 0
        APlatformInvokeCall(&thing, 0u) |> ignore
        thing
type Class1()=
[]
静态外部int APlatformInvokeCall
(
Nativent和phHandle,
uint32旗帜
)
成员this.Foo()=
让可变事物=nativeint 0
APlatformInvokeCall(&thing,0u)|>忽略
事情
错误是:

类型实例化涉及byref类型。这是通用IL规则所不允许的

奇怪的是,当我在一个模块中完成这一切时,编译错误就会消失:

module Module1 = 
    [<DllImport("somedll.dll")>]
    extern int APlatformInvokeCall
        (
            nativeint& phHandle,
            uint32 flags
        )

    let Foo() =
        let mutable thing = nativeint 0
        APlatformInvokeCall(&thing, 0u) |> ignore
        thing
模块1=
[]
外部内部应用程序调用
(
Nativent和phHandle,
uint32旗帜
)
让富()=
让可变事物=nativeint 0
APlatformInvokeCall(&thing,0u)|>忽略
事情

为什么这是作为模块编译的,而不是作为类编译的?

我认为在F#中的类中定义
extern
方法是无效的

如果您拉起并搜索
DllImport
,靠近底部的是一个表,列出了一些特殊属性以及如何使用它们。
[]
的文本说明:

当应用于模块中的函数定义时,会导致F#编译器忽略定义的实现,而将其编译为CLI p/Invoke存根声明

这似乎表明只有在模块中定义的函数上声明
extern
方法(使用
[]
)才有效;但它没有提到任何关于班级成员的内容


我想你遇到了一个编译器错误。请将此代码提交到
fsbugs@microsoft.com
这样他们就可以修复编译器发出的错误消息——在类中定义
extern
方法时应该会出现错误,因为语言规范不允许这样做。

简单的解决方案是检查规范,下面是类定义语法:

type type-name pat_opt as-defn)opt =
    class
        class-inherits-decl_opt
        class-function-or-value-defns_opt
        type-defn-elements
    end
那么我们有

class-function-or-value-defn :
      attributes_opt staticopt let rec_opt function-or-value-defns
      attributes_opt staticopt do expr
这不允许外人

这也不是你想要的


因此,我们可以看到,在试图使用时使用
extern
是无法在类内完成的。

无论这是一个无法抵抗的错误,可能就是这样的:如果
APlatformInvokeCall
被视为一个静态成员函数,则该成员只有一个元组类型的参数。元组被编译成泛型类型的对象(请参见底部的或中的5.1.3)。在这种情况下,tuple是

System.Tuple<nativeint&, uint32>
System.Tuple
但是II.9.4说不能在
byref
类型中实例化泛型类型。这解释了报告的错误


这个解释符合上面提到的事实,
Class1
在修改extern声明并调用以获取单个参数的情况下起作用(很好,编译)。它也符合模块版本工作的事实,因为在该版本中没有考虑成员函数

嗯,很有趣。奇怪的是,即使在类中,pinvoke也会编译成正确的IL。更奇怪的是,如果外部方法只有一个参数,编译就成功了。不过,你可能是对的,我会把它放在一个模块中。“因此,我们可以看到,在您尝试使用它时使用
extern
。”嗯?这是有道理的。如果我尝试使用nativent创建一个元组&它将失败,并出现相同的错误。
System.Tuple<nativeint&, uint32>