私の推測では、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}"))
- コードは、式ツリーの本体がメソッド呼び出し式 (つまり、コントローラーからのメソッドの呼び出し) であることを前提としています。
- 次に、コードはメソッド呼び出しの引数を 1 つずつ選択し、それらを引数 0 のラムダにラップし、コンパイルして実行することで、その値を取得しようとします。ライブラリは、引数の値が定数値または囲んでいる環境から取得された値であることに暗黙的に依存しています。
F# ランタイムによって生成される式ツリーの形状 (つまり、パイピングが使用される場合)
c => op_PipeRight(c.Get("x"), ToFSharpFunc(値 => 無視(値)))
これはまだメソッド呼び出し式です (したがって、仮定 1 は依然として正しいでしょう) が、その最初の引数はパラメーター c を使用します。この引数が引数なしでラムダに変換される場合 (() => c.Get("x")) - そのようなラムダのメソッド本体は、いくつかの自由変数 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}"))