将第一类函数参数传递给LINQ表达式

Ben*_*erg 3 f#

此代码段再现了我使用某些生产代码时遇到的问题.函数containsProperty表示实际上在库中的真实世界函数,因此我对签名是什么没有发言权.

问题是我无法弄清楚如何创建一个可以将普通函数作为参数的包装函数,然后将其传递给containsProperty.我可以直接使用函数作为lambda表达式调用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()
Run Code Online (Sandbox Code Playgroud)

两个问题的答案来自F#func的Expression <Func <T,bool >>对我有所帮助,但我还没有找到解决方案.

Tar*_*mil 5

所以,这里有两个问题.

如何传递函数而不必将其包装<@ ... @>

为此,您只需要将[<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))
Run Code Online (Sandbox Code Playgroud)

如何转换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)
Run Code Online (Sandbox Code Playgroud)