【问题标题】:Pass first class function parameter to LINQ expression将第一类函数参数传递给 LINQ 表达式
【发布时间】:2018-05-06 06:19:35
【问题描述】:

此代码 sn-p 重现了我在使用某些生产代码时遇到的问题。函数 containsProperty 表示一个实际存在于库中的真实世界函数,因此我对签名是什么没有发言权。

问题是我不知道如何创建一个可以将普通函数作为参数的包装函数,然后将其传递给 containsProperty。我可以直接使用函数作为 lambda 表达式调用 containsProperty,但我不能使用来自其他来源的函数调用它。

addToGroup 函数是迄今为止我想出的最好的函数,它使用引号。这种方法有两个问题,我正在努力解决。首先,我如何摆脱引用中的 Func 演员?也许以某种方式将它移到 addToGroup 中?其次,我可以以此为基础来传递一个函数吗?我没有成功找到不会产生编译时错误或运行时错误的东西。

函数 addToGroup2 是我想做的,但它不能编译。错误消息是“没有可用于类型 'Quotations.Expr' 的构造函数”。

我为什么要为此烦恼?因为只要我不能将传入的函数视为一流的值,我就无法创建我所追求的设计。我希望这些函数来自记录集合。

如果您将此 sn-p 粘贴到 LINQPad 或其他东西中,请注释掉 addToGroup2 及其对它的调用,以使 sn-p 编译和运行。

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()

Expression<Func<T, bool>> from a F# func 的两个答案都对我有所帮助,但我还没有找到解决方案。

【问题讨论】:

    标签: f#


    【解决方案1】:

    所以,这里有两个问题。

    如何传递函数而不必将其包装在&lt;@ ... @&gt;中?

    为此,您只需将[&lt;ReflectedDefinition&gt;] 属性添加到方法的参数中。它隐式地将传递给它的参数包装在引号中。

    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&lt;a -&gt; b&gt;转换成Expression&lt;Func&lt;a, b&gt;&gt;

    这确实在您链接的问题中得到了解释,尽管此后 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)
    

    【讨论】:

      猜你喜欢
      • 2010-11-14
      • 2015-06-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多