2016-11-10 47 views
3

給定一個函數列表,如何提取包含列表中每個函數的第一個參數類型的列表?在F#中,如何獲取函數的參數?

列表定義爲:

let messageHandlers = [ 
    fun (message: MessageA) ->(), // Cast 
    fun (message: MessageB) ->() // Cast 
] 

類型列表然後可以定義爲:

let getFirstParam x = x.GetType().UseReflectionToGetTheParameters 

let types = List.map getFirstParam messageHandlers 

我本來期望一個名爲Parameters或東西就FSharpFunc類似的名單,但是我找不到一個。

+2

像往常一樣,這裏真正的問題可能是您想實現的「更高」目標。根據這一點,這個細節可能是或可能不是必需的。正如@JohnPalmer所說,類型將全部相同,因爲'List'只允許具有相同類型的元素(在這種情況下,具有相同簽名的函數)。 – TeaDrivenDev

+1

把它看作模式匹配,但我想返回匹配中的類型列表,以便我可以遍歷它們。該應用程序是一個簡單的消息總線。 –

+0

難道不同的消息是一個聯合的一部分,然後有一個'消息 - >單元(或響應而不是單位,響應是可能的響應的另一個聯合)列表? – Sehnsucht

回答

4

如何獲得各類靜態代替,以避免錯誤的風險,這樣的:

let messageHandlers, types = 
    let withArgType (f: 'T -> unit) = (f :> obj, typeof<'T>) 
    [ 
     withArgType (fun (param1: MessageA) ->()) 
     withArgType (fun (param1: MessageB) ->()) 
    ] 
    |> List.unzip 
3

首先,列表不能包含不同類型的元素。因此,methods列表中的所有功能將具有完全相同的第一個參數。

不過沒關係,你可以在技術上得到解決,通過擦除的功能類型(即他們鑄造obj):

let methods = [ 
    (fun (param1: MyRecordType) ->()) :> obj 
    (fun (param1: AnotherType) ->()) :> obj 
] 

現在你已經擁有屬於自己的漂亮的obj list,其中每一個元素其實是一個功能。除了在編譯時不知道的情況,因爲你已經將它們鑄造爲obj

現在,F#中的函數由類FSharpFunc<_,_>表示。第一個通用參數是輸入,第二個輸出。所以,你可以只取第一個通用的說法,這就是你的答案:

let paramType = fn.GetType().GetGenericArguments().[0] 

除了我還要把保障到位,以確保我通過了obj實際上是一個功能:

let funcType = typeof<FSharpFunc<_,_>>.GetGenericTypeDefinition() 

let getFunctionParamType fn = 
    let fnType = fn.GetType() 
    if fnType.IsGenericType && 
     funcType.IsAssignableFrom (fnType.GetGenericTypeDefinition()) 
    then 
     Some (fnType.GetGenericArguments().[0]) 
    else 
     None 

注:有必要使用funcType.IsAssignableFrom(而不是僅僅與funcType =比較),因爲有些功能可以作爲一個自定義類的衍生從實施10。

更新:作爲KVB指出評價,爲了更固溶體,可以使用FSharpType.GetFunctionElementsFSharpType.IsFunction功能,其基本上包住上述邏輯以更方便,F# - 友好方式:

let getFunctionParamType fn = 
    if FSharpType.IsFunction fn && 
     let input, output = FSharpType.GetFunctionElements fn 
     Some input 
    else 
     None 

要當心雖然:反射是一件棘手的事情,很容易出錯,容易發生沉默故障。從你的問題來看,你並不真正瞭解它是如何工作的,這是使用它的一個強烈的禁忌。也許如果你描述了你的首要問題,有人可以提供更好的解決方案。

+0

我已經添加了一些更多的上下文,但我想保留一般問題以便其他人更有用 –

+2

如果您使用原始.NET反射,那麼使用相等檢查類型通常是錯誤的 - 您應該使用IsAssignableFrom而不是(該函數的運行時類型可以是從'_ - > _'派生出來的任何具體類型,這樣可以)。但是,在這種情況下,您不需要直接使用.NET反射 - 使用'FSharp.Reflection.FSharpType.GetFunctionElements'(如果需要,可以使用'FSharp.Reflection.FSharpType.IsFunction'來保護)。 – kvb

+0

此外,您的'funcType'定義甚至不會編譯 - 您在類型而不是方法上調用'GetGenericMethodDefinition'。您應該使用'let funcType = typedefof <_-> _>'來代替。 – kvb

2

通過Tarmil最偉大的回答啓發我最終得到的各類靜態,但我還包裹在一個更加每個功能一般功能。

let messageHandlers, types = 
    let withArgType (f: 'T -> unit) = 
     let genericFunc = fun (o: obj) -> (f (o :?> 'T)) 
     (genericFunc, typeof<'T>) 
    [ 
     withArgType (fun (message: MessageA) ->()) 
     withArgType (fun (message: MessageB) ->()) 
    ] 
    |> List.unzip