Спасибо за этот вопрос - это действительно хороший отчет об ошибке с простым воспроизведением, и я не мог в это поверить, но вы совершенно правы. Плюс работает, а минус - нет.
Проблема в том, что 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
или sub_float
), либо путем написания собственной динамической реализации sub
(что, вероятно, может быть сделано довольно эффективно с использованием DLR (см. почта ).