2017-07-21 25 views
3

我的F#代碼的某些功能接收到的值作爲對象裝箱,即使底層值是鍵入的。如果這個值是一個有區別的聯合,那麼就不可能將它重新裝回到它的F#類型中。下面是一個簡單的例子:F#歧視工會的拆箱價值

type Result<'TOk,'TError> = 
| Ok of 'TOk 
| Error of 'TError 

type ResultA = Result<string, int> 

let a = Ok "A" 
let o = box a 

match o with 
| :? ResultA -> printfn "match ResultA" 
// | :? ResultA.Ok -> printfn "match" // doesn't compile 
| _ when o.GetType().DeclaringType = typedefof<ResultA> -> printfn "match via reflection" 
| _ -> printfn "no match" 

從這個例子的輸出爲「經由反射匹配」,ResultA永遠沒有匹配,因爲已裝箱值是不同的CLR類型的 - Result.Ok。由於F#歧義聯合事例被表示爲其自己的類型,因此裝箱值與ResultA類型不匹配。此外,無法將它與ResultA.OK匹配,因爲在F#代碼中它不是合法類型。唯一的選擇似乎是使用反射手動實例化一個值,這是低效率和愚蠢的,因爲該值已經實例化,它就在這裏,一旦它被裝箱,它就無法在F#代碼中被訪問。

我可以俯視嗎?有沒有更直接的方式拆箱F#歧視聯合價值?

回答

4

你只是匹配不同的類型。您的變量a而不是ResultA類型,但是泛型類型爲Result<string, 'a>,當裝箱​​時被強制爲Result<string, obj>

要麼使變量有明確的權利類型:

let a : ResultA = Ok "A" 

或搭配正確的類型:

match o with 
| :? Result<string, obj> -> printfn "match ResultA" 

兩個選項會工作。


你的假設的說明:

ResultA永遠沒有匹配,因爲裝箱的值是不同的CLR類型 - Result.Ok

這是不原因。與類型匹配的工作方式與C#中的is/as運算符相同 - 即匹配子類型和確切類型。 DU成員被編譯爲DU類型本身的子類型。這就是F#如何讓.NET處理不同情況的一種類型。

對一般運行時類型的說明:
處理類型在運行時不應該是必要的。儘可能地避免它。經驗法則應該是,如果你發現自己在運行時處理類型,你可能會模擬出錯的東西。

尤其如此,如果你不知道究竟是怎麼一切正常。

+0

感謝Fyodor。其實你的第一個建議沒有奏效 - 使變量正確的類型明確地導致了相同的盒裝類型並且不匹配。但第二個工作 - 改變模式匹配的情況。 –

+0

您在嘗試第一個選項時一定犯了錯誤。它確實有效。 –

+0

我完全同意你的預防措施。不幸的是,由於外部庫的細節,我必須處理幾個盒裝值。 –