我的猜测是它应该在 F# 3.1 中修复。这是来自 VS2013 预览版
type T = static member Get(e : System.Linq.Expressions.Expression<System.Action<'T>>) = e
type U = member this.MakeString() = "123"
T.Get(fun (u : U) -> ignore(u.MakeString())) // u => Ignore(u.MakeString())
更新:无法从问题中检查实际库,所以我会尝试模仿我看到的界面。此代码在 F# 3.1 中运行良好
open System
open System.Linq.Expressions
type Linker() =
member this.GetUri<'T>(action : Expression<Action<'T>>) : string = action.ToString()
type Model() = class end
type Controller() =
member this.Get(s : string) = Model()
let linker = Linker()
let text1 = linker.GetUri<Controller>(fun c -> c.Get("x") |> ignore) // c => op_PipeRight(c.Get("x"), ToFSharpFunc(value => Ignore(value)))
let text2 = linker.GetUri<Controller>(fun c -> ignore(c.Get("x"))) // c => Ignore(c.Get("x"))
printfn "Ok"
更新 2:我查看了 Hyprlinkr 的源代码,我想我找到了原因。分析表达式树的库代码的当前实现正在对其形状做出某些假设。尤其:
// C#
linker.GetUri((c : Controller) => c.Get("{file-name}"))
- 代码假设表达式树的主体是方法调用表达式(即从控制器调用某些方法)
- 然后代码一一挑选方法调用参数,并尝试通过将它们包装到 0 参数 lambda 中来获取其值,然后编译并运行它。库隐含地依赖于参数值是常量值或从封闭环境中捕获的值。
F# 运行时(即使用管道时)生成的表达式树的形状将是
c => op_PipeRight(c.Get("x"), ToFSharpFunc(value => Ignore(value)))
这仍然是方法调用表达式(因此假设 1 仍然正确),但它的第一个参数使用参数 c。如果此参数将转换为不带参数的 lambda (() => c.Get("x")) - 那么此类 lambda 的方法体将引用某个自由变量 c - 正是异常消息中所写的内容。
作为对 F# 更友好的替代方案,我可以建议为 GetUri 添加额外的重载
public string GetUri<T, R>(Expression<Func<T, R>> e)
它可以同时用于 C# 和 F# 端
// C#
linker.GetUri((Controller c) => c.Get("{filename}"))
// F#
linker.GetUri(fun (c : Controller) -> c.Get("{filename}"))