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

How can I create a measured record type without a measure in F#

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

然后我在 F# 库 (LibF) 使用的 C# 库 (LibC) 中写了一个 public 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

错误 FS0001:类型不匹配。期望 'Vec2' 但给定 'Vec2<1>' 元组有 0 和 1 的不同长度

我试过:

有人知道以互操作友好的方式构建测量记录类型的方法吗?

如评论中所述,度量单位是 F# 独有的功能,在编译的 .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# 使用没有单位的单独类型。

Tomas 的回答是正确的。我会为后代分享我的解决方法。

我的测量单位是

 [<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 以执行数学运算,但将 'concrete' 类型用于 public API。有额外的类型有点烦人,但它有效。