输入静态方法的约束

时间:2014-01-04 20:44:35

标签: f#

我正在使用OpenTK及其数学库,但不幸的是,矢量类没有通用接口。例如,Vector2,3和4都具有相同的静态方法SizeInBytes http://www.opentk.com/files/doc/struct_open_t_k_1_1_vector3.html#ae7cbee02af524095ee72226d842c6892

现在我可以重载大量不同的构造函数,但我认为应该可以通过类型约束解决这个问题。

我正在阅读http://msdn.microsoft.com/en-us/library/dd233203.aspx,我发现了这个

type Class4<'T when 'T : (static member staticMethod1 : unit -> 'T) > =
    class end

现在我自己尝试了,但我无法正确使用语法。

type Foo<'T when 'T: (static member SizeInBytes: unit -> int)>(data: 'T []) =
   member this.GetBytes() = 'T.SizeInBytes() 

let f = Foo([|new Vector3(1.0f,1.0f,1.0f)|])
f.GetBytes()

你能发现问题吗?

编辑: VS2012抱怨此行'T.SizeInBytes() //Unexpected symbol or expressionT.SizeInBytes()也无效。

EDIT2:

我做了一个不涉及外部库的例子

type Bar() = 
    static member Print() = printf "Hello Foo"

type Foo<'T when 'T: (static member Print: unit -> unit)>(data: 'T []) =
   member this.Print() = 'T.Print()

let b1 = Bar()
let f = Foo([|b1|])
f.Print()

2 个答案:

答案 0 :(得分:6)

调用由成员约束保证的事物的正确语法有点模糊:

type Foo< ^T when ^T: (static member SizeInBytes: unit -> int)>(data: ^T []) =
   member inline this.GetBytes() =
       (^T : (static member SizeInBytes : unit -> int) ())

请注意,'T必须更改为“静态解析的类型变量”^T - 请参阅F# spec中的词汇表。

您不能调用由普通类型变量的约束指定的成员,因为.NET框架不支持它,因此F#必须将它们编译掉。如果我们尝试在'T中使用GetBytes,则会出现语法错误。

我认为MSDN文档通过给出'T的示例有点误导,因为尽管您可以编写它们提供的类型,但您永远不能使用约束。

如果查看Class4示例的IL代码,则约束实际上已经消失:

.class nested public auto ansi serializable Class4`1<T>
    extends [mscorlib]System.Object

这是有道理的,因为必须为.NET删除成员约束。具有type Foo类型变量的^T也是如此。

另请注意,与所有inline F#函数一样,您只能通过F#代码静态调用它,以便编译器可以在调用站点内联定义。

如果您尝试从C#代码或通过反射调用它,它将抛出异常。如果您尝试,您的代码将在运行时失败。

通常使用.NET不支持的F#约束是一项棘手的工作,所以如果可能的话,我会明确指出。

编辑:根据(a)我的进一步实验(b)Gene Belitski的答案和(c)idjarn关于inline函数的评论,我已经大大更新了我的原始答案,错误地说这是不可能的。总是被编译到引发异常的IL。

答案 1 :(得分:4)

使用hat notation

为我工作得很好
type Bar() =
    static member SizeInBytes() = 42

type Foo< ^T when ^T: (static member SizeInBytes: unit -> int)>(data: ^T []) =
    member inline this.GetBytes () = (^T : (static member SizeInBytes : unit -> int) ())

let result = (Foo([|Bar()|]).GetBytes())

val result : int = 42