感谢这个问题 - 这是一个非常好的错误报告,带有一个简单的重现,我无法相信这一点,但你是完全正确的。加号有效,但减号无效。
问题是sub
和add
被编译为泛型方法,而 LINQ 版本调用这些泛型方法。内联是在存储引用后执行的,因此引用的代码包含对sub
方法的调用。这在普通 F# 代码中不是问题,因为函数是内联的,并且运算符在某些数字类型上被解析为 + 或 -。
但是,通用版本使用动态查找。如果您查看prim-types.fs:3530
,您会看到:
let inline (+) (x: ^T) (y: ^U) : ^V =
AdditionDynamic<(^T),(^U),(^V)> x y
when ^T : int32 and ^U : int32 = (# "add" x y : int32 #)
when ^T : float and ^U : float = (# "add" x y : float #)
// ... lots of other cases
这AdditionDynamic
是从泛型方法调用的内容。它进行动态查找,这会比较慢,但它会起作用。有趣的是,对于减号运算符,F# 库不包含动态实现:
[<NoDynamicInvocation>]
let inline (-) (x: ^T) (y: ^U) : ^V =
((^T or ^U): (static member (-) : ^T * ^U -> ^V) (x,y))
when ^T : int32 and ^U : int32 = (# "sub" x y : int32 #)
when ^T : float and ^U : float = (# "sub" x y : float #)
// ... lots of other cases
我不知道为什么会这样 - 我认为没有任何技术原因,但它解释了为什么你会得到你报告的行为。如果您使用 ILSpy 查看编译后的代码,您会看到该add
方法做了一些事情,而该sub
方法只是抛出了(所以这就是异常的来源)。
至于解决方法,您需要以不使用通用减号运算符的方式编写代码。最好的选择可能是避免inline
使用函数(使用sub_int
or sub_float
)或编写自己的动态实现sub
(使用 DLR 可能非常有效地完成(参见这篇文章)。