Не удается получить перегрузку оператора для работы с деревьями выражений Linq

Я создаю деревья выражений Linq из F #, которые работают с имеющимся у меня настраиваемым типом данных. Тип представляет собой очень простое размеченное объединение, в котором обычные арифметические операторы перегружены. Но по какой-то причине я не могу создавать узлы арифметических выражений linq из-за того, что не может найти правильную перегрузку. Дело в том, что, клянусь, у меня это работало некоторое время назад, но я не могу понять, что я изменил, чтобы заставить его сломаться.

Прилагаю небольшой образец кода, показывающий проблему. В приведенном ниже типе данных оператор сложения перегружен. Использование перегруженного оператора работает как шарм, но когда я пытаюсь создать узел дерева выражения сложения с помощью Expression.Add (lhs, rhs), система выдает исключение с жалобой на то, что не может найти перегрузку для операции добавления.

Кто-нибудь знает, что я делаю не так?

Спасибо, Рикард

open System.Linq.Expressions

module DataType =
    exception NotImplementedYet of string

    type DataCarrier =
        | ScalarCarrier of float
        | VectorCarrier of float array

        member this.Add(other) =
            match (this, other) with
            | ScalarCarrier(x), ScalarCarrier(y) -> ScalarCarrier(x + y)
            | VectorCarrier(u), VectorCarrier(v) -> 
                VectorCarrier(Array.map2 (fun x y -> x + y) u v)
            | _,_ -> raise (NotImplementedYet("No go!"))

        static member (+) (lhs:DataCarrier, rhs) =
            lhs.Add(rhs)

module Main =
    let createAddOp (lhs:DataType.DataCarrier) (rhs:DataType.DataCarrier) =
        let clhs = Expression.Constant(lhs)
        let crhs = Expression.Constant(rhs)
        Expression.Add(clhs, crhs)

(* no problems with this one *)
printf "Testing operator overloading: %A" (DataType.ScalarCarrier(1.0) 
                                           + DataType.ScalarCarrier(2.0))
(* this throws an exception *)
printf "Testing expr construction %A" (Main.createAddOp 
                                        (DataType.ScalarCarrier(1.0))
                                        (DataType.ScalarCarrier(2.0)))

person Rickard    schedule 07.11.2009    source источник


Ответы (1)


Одно из решений - явно ввести операнды Expression (присвоив им статический тип DataType.DataCarrier вместо их типа времени выполнения DataType.DataCarrier.ScalarCarrier):

module Main =
    let createAddOp (lhs:DataType.DataCarrier) (rhs:DataType.DataCarrier) =
        let clhs = Expression.Constant(lhs, typeof<DataType.DataCarrier>)
        let crhs = Expression.Constant(rhs, typeof<DataType.DataCarrier>)
        Expression.Add(clhs, crhs)

Другой вариант - явно передать используемый оператор сложения:

module Main =
    let createAddOp (lhs:DataType.DataCarrier) (rhs:DataType.DataCarrier) =
        let clhs = Expression.Constant(lhs)
        let crhs = Expression.Constant(rhs)
        Expression.Add(clhs, crhs, typeof<DataType.DataCarrier>.GetMethod("op_Addition"))

Однако я удивлен, что ваш исходный код не работает. Похоже, что это ограничение в том, как деревья выражений находят соответствующие операторы добавления (то есть, похоже, что Linq ищет только операторы добавления в типах операндов во время выполнения).

person kvb    schedule 07.11.2009
comment
Потрясающе, я действительно думал, что обходных путей не существует. Вы только что избавили меня от стресса! +1 Я думаю, что это работало до октябрьской CTP F #. Я проведу небольшое тестирование. - person Rickard; 07.11.2009