2011-07-26 47 views
5

如何使用反射創建Action<'T>的實例?下面是我有:使用反射創建動作<'T>的實例

let makeAction (typ:Type) (f:'T -> unit) = 
    let actionType = typedefof<Action<_>>.MakeGenericType(typ) 
    let converter = FSharpFunc.ToConverter(f) 
    Delegate.CreateDelegate(actionType, converter.Method) 

與barfs:

System.ArgumentException:錯誤綁定到目標方法。
在System.Delegate.CreateDelegate(類型類型,MethodInfo的方法,布爾throwOnBindFailure)

'T是一個接口,它typ器具。

+0

是否有任何特定的原因想要使用反射來做到這一點?因爲這可以簡單地完成:'make makeAction(f:'a - > unit)= new Action <'a>(f)' – Ankur

+0

@Ankur:是的,因爲我不知道'a'(在你的例子中)在編譯時。 – Daniel

+0

不知道我是否正確地得到了你,但這裏的''a''與你的代碼中''T'相同,即一個通用類型,它根據傳遞的'f'值被解析。你需要一個包含傳遞的'f'函數的動作類型? – Ankur

回答

3

我覺得有兩個問題。第一個是你需要調用CreateDelegate重載,它有三個參數。附加參數指定應在其上調用方法的實例。

第二個問題是Converter<'T, unit>實際上編譯爲返回Microsoft.FSharp.Core.Unit的方法,而不是返回void的方法。我不確定是否有更簡單的解決方法,但是您可以定義包含方法的包裝器。成員編譯看起來像C#,所以單位類型將在這種情況下被編譯爲void

open System 

type Wrapper<'T>(f:'T -> unit) = 
    member x.Invoke(a:'T) = f a 

let makeAction (typ:Type) (f:'T -> unit) = 
    let actionType = typedefof<Action<_>>.MakeGenericType(typ) 
    let wrapperType = typedefof<Wrapper<_>>.MakeGenericType(typ) 
    let wrapped = Wrapper<_>(f) 
    Delegate.CreateDelegate(actionType, wrapped, wrapped.GetType().GetMethod("Invoke")) 

makeAction (typeof<int>) (printfn "%d") 

編輯 - 難道一個小的改動,使之能在您的場景中工作(含接口)

+0

太棒了。謝謝!我只是在創建'Converter'來獲取'MethodInfo'。我不知道如何從F#函數中獲取它。 – Daniel

+0

@Daniel - 如果你有一個函數'f',你也可以使用'f.GetType()。GetMethod(「Invoke」)',但是這會給你一個返回'unit'的方法,所以它不起作用在這種情況下。 –

+0

是的,我首先嚐試了並做出了同樣的發現。 – Daniel