Передать параметр функции первого класса в выражение LINQ - PullRequest
0 голосов
/ 06 мая 2018

Этот фрагмент кода воспроизводит проблему, с которой я столкнулся при работе с производственным кодом. Функция содержитProperty представляет функцию реального мира, которая фактически находится в библиотеке, так что я не могу сказать, что это за сигнатура.

Проблема в том, что я не могу понять, как создать функцию-обертку, которая может принять нормальную функцию в качестве аргумента, а затем передать ее в containsProperty. Я могу вызывать containsProperty напрямую с помощью функции в качестве лямбда-выражения, но я не могу вызывать ее с помощью функции из какого-то другого источника.

Функция addToGroup - лучшая из всех, что я придумал, и она использует цитаты. У этого подхода есть две проблемы, которые я пытаюсь выяснить. Во-первых, как мне избавиться от броска Func в цитате? Возможно, как-нибудь переместить его в addToGroup? Во-вторых, могу ли я опираться на это, чтобы просто передать функцию? Мне не удалось найти то, что не приводит ни к ошибке времени компиляции, ни к ошибке времени выполнения.

Функция addToGroup2 - это то, что я хотел бы сделать, но она не компилируется. Сообщение об ошибке: «Нет доступных конструкторов для типа 'Quotations.Expr <' a> '".

Почему я пытаюсь бороться с этим? Потому что, пока я не могу обработать переданную функцию как значение первого класса, я не могу создать дизайн, который мне нужен. Я хочу, чтобы эти функции пришли из коллекции записей.

Если вы вставите этот фрагмент в LINQPad или что-то еще, закомментируйте addToGroup2 и вызовы к нему, чтобы этот фрагмент компилировался и выполнялся.

open System
open System.ComponentModel
open System.ComponentModel.DataAnnotations // Reference to this assembly required.

type CfgSettings = {
    mutable ConnectionString: string
    mutable Port: int
    }

and CfgSettingsMetadata() =
    static member containsProperty<'TProperty>(propertyExpression: Linq.Expressions.Expression<Func<CfgSettings,'TProperty>>) =
        Console.WriteLine "good!"
    static member addToGroup f =
        CfgSettingsMetadata.containsProperty(FSharp.Linq.RuntimeHelpers.LeafExpressionConverter.QuotationToLambdaExpression f) |> ignore
    static member addToGroup2 (f: CfgSettings -> 'TProperty) =
        CfgSettingsMetadata.containsProperty(FSharp.Linq.RuntimeHelpers.LeafExpressionConverter.QuotationToLambdaExpression (Quotations.Expr<Func<CfgSettings,'TProperty>>f)) |> ignore
    static member BuildMetadata () =
        CfgSettingsMetadata.containsProperty(fun x -> x.ConnectionString)
        CfgSettingsMetadata.containsProperty(fun x -> x.Port)
        CfgSettingsMetadata.addToGroup <@ Func<_,_>(fun x -> x.ConnectionString) @>
        CfgSettingsMetadata.addToGroup <@ Func<_,_>(fun x -> x.Port) @>
        CfgSettingsMetadata.addToGroup2 (fun x -> x.ConnectionString)
        CfgSettingsMetadata.addToGroup2 (fun x -> x.Port)

CfgSettingsMetadata.BuildMetadata()

Оба ответа на вопрос Выражение > из F # func несколько помогло мне, но я пока не нашел решения.

1 Ответ

0 голосов
/ 06 мая 2018

Итак, здесь есть два вопроса.

Как передать функцию без ее переноса в <@ ... @>?

Для этого вам просто нужно добавить атрибут [<ReflectedDefinition>] к параметру вашего метода. Он неявно оборачивает переданный ему аргумент в кавычку.

type CfgSettingsMetadata() =
    static member addToGroup([<ReflectedDefinition>] f: Expr<CfgSettings -> 'TProperty>) =
        CfgSettingsMetadata.containsProperty(LeafExpressionConverter.QuotationToLambdaExpression f) |> ignore

// Example use:
CfgSettingsMetadata.addToGroup(Func<_, _>(fun x -> x.ConnectionString))

Как конфертировать из Expr<a -> b> в Expression<Func<a, b>>?

Это действительно объясняется в вопросе, который вы связали, хотя API немного изменился с тех пор.

type CfgSettingsMetadata() =
    static member addToGroup ([<ReflectedDefinition>] (f: Expr<CfgSettings -> 'TProperty>)) =
        let call = LeafExpressionConverter.QuotationToExpression f :?> MethodCallExpression
        let lambda = call.Arguments.[0] :?> LambdaExpression
        let e = Expression.Lambda<Func<CfgSettings, 'TProperty>>(lambda.Body, lambda.Parameters)
        CfgSettingsMetadata.containsProperty(e) |> ignore

// Example use:
CfgSettingsMetadata.addToGroup(fun x -> x.ConnectionString)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...