比方說,我們有一個簡單的F#報價:生成參數F#報價
type Pet = { Name : string } let 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 (Some (x), System.String Name, []))
現在我想概括它,所以我代替型「寵物「和屬性」名稱「我可以使用任何類型和方法/屬性定義它。這裏是我想要做的事:
let 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]], delegateArg, Application (Lambda (x, PropertyGet (Some (x), System.String Name, [])), delegateArg))
正如你所看到的,第一和第二個表達式之間的區別是,在第一種情況下頂級NewDelegate表達式包含PropertyGet,而第二個表達式將PropertyGet包裝在Application/Lambda表達式中。當我將這個表達式傳遞給外部代碼時,它並不期望這樣的表達式結構並失敗。所以我需要一些方法來構建一個通用版本的引用,所以當它被專門化時,得到的引用是完全匹配的< @@ System.Func(fun(x:Pet) - > x.Name) @@ >。這可能嗎?或者只有選擇手動將模式匹配應用到生成的報價並將其轉換爲我需要的選項?
UPDATE。作爲一種變通方法,我實現了以下接口:
let convertExpr (expr : Expr) = match expr with | NewDelegate(t, darg, appl) -> match (darg, appl) with | (delegateArg, appl) -> match appl with | Application(l, ldarg) -> match (l, ldarg) with | (Lambda(x, f), delegateArg) -> Expr.NewDelegate(t, [x], f) | _ -> expr | _ -> expr | _ -> expr
它的工作 - 我現在可以從第1轉換表達形式第二。但我有興趣查明這是否可以以簡單的方式實現,而不需要遍歷表達式樹。
謝謝你的回答和一個很好的建議。然而,我仍然堅持相關的問題:是否有可能將簡單的F#委託(例如fun x - > x.Name)插入到與實際類型無關的通用引用中,如上面的引用#2。這與模擬框架的作用類似:它們定義一些表達式而不知道具體的接口並注入具體的類型。我似乎無法在F#中實現這一點。 – 2010-08-05 17:41:51
@Vagif - 我不知道我理解你的問題。你能詳細談談你想做什麼嗎? – kvb 2010-08-05 18:51:31
我需要在F#和C#之間發送interop。 C#預計某種類型的LINQ表達式。我可以用硬編碼的方式在F#中編寫它,但我想爲此構建一個通用的F#包裝器。這個包裝器應該能夠像「fun x - > x.Name」一樣輸入lambdas並將它們轉換爲引號。我開始懷疑在一般情況下這是不可能的。 – 2010-08-05 19:35:35