假设我们有一个简单的 F# 引用:
类型宠物 = { 名称:字符串 } 让 exprNonGeneric = <@@ System.Func(fun (x : Pet) -> x.Name) @@>
得到的报价是这样的:
val exprNonGeneri : Expr = NewDelegate (System.Func`2[[FSI_0152+Pet, FSI-ASSEMBLY, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken =b77a5c561934e089]], x,PropertyGet(一些(x),System.String名称,[]))
现在我想概括它,所以我可以使用在其上定义的任意类型和方法/属性,而不是输入“Pet”和属性“Name”。这是我正在尝试做的事情:
让 exprGeneric<'T, 'R> f = <@@ System.Func<'T, 'R>( %f ) @@> let exprSpecialized = exprGeneric<Pet, string> <@ (fun (x : Pet) -> x.Name) @>
结果表达式现在不同了:
val exprSpecialized : Expr = NewDelegate (System.Func`2[[FSI_0152+Pet, FSI-ASSEMBLY, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken =b77a5c561934e089]], 委托Arg, 应用程序(拉姆达(X, PropertyGet(一些(x),System.String名称,[])), 委托Arg))
如您所见,第一个和第二个表达式之间的区别在于,在第一种情况下,顶级 NewDelegate 表达式包含 PropertyGet,而第二个表达式将 PropertyGet 包装在 Application/Lambda 表达式中。当我将此表达式传递给外部代码时,它不会期望这种表达式结构并失败。
所以我需要一些方法来构建一个通用版本的引用,所以当它被专门化时,得到的引用是 <@@ System.Func(fun (x : Pet) -> x.Name) @@> 的精确匹配。这可能吗?还是只能选择手动将模式匹配应用于生成的报价并将其转换为我需要的?
更新。作为一种解决方法,我实现了以下适配器:
让 convertExpr (expr : Expr) = 匹配 expr | NewDelegate(t, darg, appl) -> 匹配 (darg, appl) 与 | (delegateArg, appl) -> 匹配应用程序 | 应用程序(l,ldarg)-> 匹配 (l, ldarg) 与 | (Lambda(x, f), delegateArg) -> Expr.NewDelegate(t, [x], f) | _ -> 表达式 | _ -> 表达式 | _ -> 表达式
它完成了这项工作 - 我现在可以将表达式从第一种形式转换为第二种形式。但我有兴趣找出这是否可以通过简单的方式实现,而无需遍历表达式树。