【问题标题】:Type constraints for static methods静态方法的类型约束
【发布时间】:2014-01-04 20:44:34
【问题描述】:

我正在使用 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() 也不起作用。

编辑2:

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

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()

【问题讨论】:

  • 您能详细说明问题所在吗?
  • @GaneshSittampalam 我添加了错误消息。

标签: f#


【解决方案1】:

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

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# 必须将它们编译掉。如果我们尝试在 GetBytes 中使用 'T,则会出现语法错误。

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

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

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

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

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

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

通常使用 .NET 不支持的 F# 约束是一件棘手的事情,所以如果可能的话,我会尽量避开。

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

【讨论】:

  • 所有 inline 函数编译为 IL 并抛出该异常,因为当 F# 以外的语言尝试调用它们时。只要从 F# 调用该函数,它就可以正常工作(内联函数代码存储在元数据而不是 IL 中)。
  • 谢谢 - 我以前没有意识到会发生这种情况。我想知道为什么编译器不完全忽略它们,让调用者认为他们可以做到然后在运行时抛出似乎不太友好。
【解决方案2】:

使用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

【讨论】:

  • 确实如此(我想我被
  • 不,它也工作正常在编译的形式!切换到^T 后,您是否尝试过查看 IL?我猜你没有。
  • 是的,在我发表评论之前,我确实在切换后查看了 IL。我的意思是,如果你试图在 F# 编译器没有内联使用的情况下调用它,那么它会抛出。
  • 刚刚通过从 C# 项目中调用它进行了测试,它确实抛出了。
  • 同意它在 F# 编译器无法 inline 的情况下不起作用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-06
  • 1970-01-01
相关资源
最近更新 更多