首页 文章

如何在F#中没有度量的情况下创建测量记录类型

提问于
浏览
3

我在C#(LibC)和F#(LibF)代码使用的公共库(LibRoot)中定义了一个带有度量单位的记录类型 .

然后我在F#库(LibF)消耗的C#库(LibC)中编写了一个公共API . 但是任何时候我试图将一个对象传递给这个API F#抱怨它必须有一个度量单位 .

//LibRoot - F#
type Vec2<[Measure] 'u> = {
    X : int
    Y : int
}
//LibC - C#
public static class Funcs
{
    public void DoWork(Vec2 vec) { //no measure needed in C#
        ....
    }
}
//LibF - F#

open LibC
let myVec : Vec2<1> = { X = 123; Y = 456 }
DoWork(myVec) //FS0001

error FS0001: Type mismatch. Expecting a 'Vec2' but given a 'Vec2<1>' The tuples have differing lengths of 0 and 1

我试过了:

  • Vec2<1> :FS0001

  • Vec2<0> :类型中的文字无效

  • Vec2<()> :类型中出现意外')'

  • Vec2<_> :FS0001

  • Vec2<unit> :预期的计量单位,而不是类型

  • (Vec2) myVec :没有可用于类型'Vec2<' u>的构造函数

Does anybody know a way to construct a measured record type in an interop friendly way?

2 回答

  • 1

    正如评论中所提到的,度量单位是F#-only功能,在编译的.NET代码中没有任何表示,因此当您使用C#中的单元注释类型时,它将显示为没有单位的类型 .

    已编译的C#代码将不包含特殊的F#元数据,以指示这是一个单元注释类型,因此引用您的C#库的F#编译器不会将其识别为单元注释类型 . 可以说,F#编译器可能更聪明并想出来(因为它可以发现 Vec2 类型最初来自F#) .

    我不认为有一种安全的方法可以将未测量的 Vec2 转换为测量的 Vec2<_> ,但使用 unbox 的不安全转换将在运行时工作:

    let v : LibRoot.Vec2<1> = { LibRoot.Vec2.X = 1; Y = 2 }
    LibCs.Funcs.DoWork(unbox v)
    

    我不认为有一种方法可以在F#代码中明确引用类型 Vec2 (没有度量),但 unbox 推断类型罚款 . 这不是很好,所以如果你经常需要这种转换,最好重新设计你的库,以便C#使用一个没有单位的单独类型 .

  • 3

    托马斯的回答是正确的 . 我将与后人分享我的解决方法 .

    我的衡量单位是

    [<Measure>] type Absolute
     [<Measure>] type Relative
    

    我介绍了'concrete'记录类型,可以转换为 Vec2<> 用于互操作 . 例如

    type Offset2 = { 
       X : int
       Y : int 
    }
       with 
           static member ofVec (vec : Vec2<Relative>) : Offset2 = { X = vec.X; Y = vec.Y }
           static member toVec (off: Offset2) : Vec2<Relative> = { X = off.X; Y = off.Y }
    

    我将它们转换为Vec2来执行数学运算,但是对公共API使用“具体”类型 . 有额外的类型有点烦人,但它的工作原理 .

相关问题