2012-11-13 44 views
4

我以'a option when 'a :> IBaseType的形式得到了一些計算結果。有從IBaseType派生的類型的樹,我不知道這個是什麼特定類型的選項,但重要的是它是特定派生的,而不是基類型的選項。所以我想把它上傳到IBaseType option來進一步處理它。由於選項是泛型類型,因此不可能直接進行轉換(在F#中),我必須在Option.map中進行轉換。沒有什麼複雜的,類型推斷按預期工作... inference ok 中間鑄造選項也按預期方式解決... intermediate inference ok 直到功能完成。此時由於某種原因,類型推斷決定,原來的選項必須是已經IBaseType option類型: inference not ok如何推廣f#選項?

中級類型已經早解決了,爲什麼決定重新分配infered類型op的?當然這會導致運行時異常。看起來像編譯器錯誤,但主要規則是編譯器中沒有錯誤。

所以最後它聽起來真的很愚蠢:我不知道如何簡單地上傳簡單的選項。只是爲了使圖片更清晰:processResult需要IBaseType option作爲參數。這裏是麻煩的功能來源:

(fun (x: obj) -> 
    let op = x :?> _ option 
    let upcastOp = op |> Option.map (fun y -> y :> IBaseType) 
    upcastOp |> processResult) 

任何想法如何處理這個?

+0

這是哪個編輯?看起來非常熟悉,但我找不到它。 –

+1

你能改變'processResult'來接受'選項<#IBaseType>'嗎? – Daniel

+1

@RamonSnir:這是Visual Studio,只是很少調整:[黑曜石主題之子](http://studiostyl.es/schemes/son-of-obsidian-modified-for-res- harper),[Envy Code R字體]( http://damieng.com/blog/2008/05/26/envy-code-r-preview-7-coding-font-released)和[View Whitespace on](http://blogs.msdn.com/b /zainnab/archive/2012/06/05/9990405.aspx)。 –

回答

1

我不知道怎麼的op類型在這裏推斷。 但我敢肯定,如果您不能像kvb所建議的那樣將x的類型更改爲IBaseType option,那麼您確實必須使用反射。

甲替代基於反射溶液:

let f (x:obj) = 
    match x with 
    | null -> None // x is None 
    | _ -> match x.GetType().GetProperty("Value") with 
      | null -> None // x is not an option 
      | prop -> 
       let v = prop.GetValue(x, null) 
       Some (v :?> IBaseType) 
3

你是如何生產盒裝物品的?最簡單的解決方案是將IBaseType option,而不是拳擊#IBaseType option開始。如果出於某種原因不可行,那麼您可能需要使用反射。問題是,在這個代碼塊:

let op = x :?> _ option 
let upcastOp = op |> Option.map (fun y -> y :> IBaseType) 

編譯器知道op是一些'a一個'a option when 'a :> IBaseType但沒有什麼可以讓編譯器來找出'a其實是因爲這種類型的不是體現在你的函數的最終輸出 - 編譯器需要承諾特定的類型爲'a,並且它可以做出的最佳猜測僅僅是基類型IBaseType。你需要做這樣的事情,而不是:

type ResultProcessor = 
    static member ProcessResult<'a when 'a :> IBaseType> (op:'a option) = 
     let upcastOp = op |> Option.map (fun y -> y :> IBaseType) 
     upcastOp |> processResult 

fun (x:obj) -> 
    let ty = x.GetType() // must be option<something> 
    let [| tyArg |] = ty.GetGenericArguments() 
    typeof<ResultProcessor>.GetMethod("ProcessResult").MakeGenericMethod(tyArg).Invoke(null, [|x|])  
+0

初始值本身是反射執行的結果。而且我對結果類型沒有影響。而關於類型推斷 - 我仍然不清楚爲什麼類型推斷決定了op必須具有這種特定類型。最後的聲明沒有增加關於它的知識。 –

+0

@AndriyK - 你正在做什麼反射電話來產生價值?你可以修改被反射調用的方法,以便它返回一個'IBaseType選項'而不是'SomeSubType選項'?如果是這樣,那將是最簡單的。 – kvb

+1

+1我認爲我在使用反射處理各種F#類型時寫了一個類似這樣的次數的函數:-)這種反射方法對於除選項之外的其他類型也很適用,如果遇到類似的情況, F#列表。 –

1

我支持KVB的溶液。現在,在我爲類似代碼所做的一些基準測試中,爲了獲得絕對性能,我發現它有利於避免動態(未知)方法調用。不知何故,通用類型的新實例化速度更快。例如:

[<AbstractClass>] 
type BaseResultProcessor() = 
    abstract member ProcessResult : obj -> option<IBaseType> 

[<Sealed>] 
type ResultProcessor<'T when 'T :> IBaseType>() = 
    inherit BaseResultProcessor() 
    override this.ProcessResult(x: obj) = 
     match x :?> option<'T> with 
     | Some x -> Some (x :> IBaseType) 
     | None -> None 

module Example = 
    let run (x: obj) = 
     let ty = x.GetType() 
     let tyArg = ty.GetGenericArguments().[0] 
     let p = 
      typedefof<ResultProcessor<_>>.MakeGenericType(tyArg) 
      |> Activator.CreateInstance :?> BaseResultProcessor 
     p.ProcessResult(x) 

至於是什麼問題,下面的「直覺」推理是無效的。NET:

'T1 :> 'T2 
-------------------------- 
option<'T1> :> option<'T2> 

我會說這是典型的類型系統 - 即易出現的或直觀是很難或不可能的正確實施,一旦你考慮到與類型系統作爲一個整體及其相互作用的事情。